Date: Fri, 12 Mar 1999 16:30:45 -0800 From: Patrick Powell <papowell@astart2.astart.com> To: freebsd-small@freebsd.org Subject: How2Build Update Message-ID: <36E9B1B5.3AD7E06C@astart.com>
next in thread | raw e-mail | index | archive | help
This is a multi-part message in MIME format. --------------EBAC1F2D12499441013FE952 Content-Type: text/plain; charset=us-ascii Content-Transfer-Encoding: 7bit This is a modified version of the how2build.html page in /usr/src/release/picobsd/doc/src/how2build.html I have added some corrections and additions, including a section on how FreeBSD 3.1-RELEASE sets up a two floppy boot system. Patrick Powell --------------EBAC1F2D12499441013FE952 Content-Type: text/html; charset=us-ascii; name="00049D62.html" Content-Transfer-Encoding: 7bit Content-Disposition: inline; filename="00049D62.html" Content-Base: "file:///C|/WINDOWS/TEMP/00049D62.html" <html> <! $Id: how2build.html,v 1.4 1998/11/01 20:01:40 abial Exp $ > <head> <title><center>PicoBSD Development Kit</center></title> </head> <body> <h1><center>How to Build Your Own Version of PicoBSD </center></h1> <h2><center> Andrzej Bialecki <tt><abial@freebsd.org></tt></center></h2> <h2><center>Updated and Extended by Patrick Powell <tt><papowell@astart.com></tt> </center></H2> <A HREF=#build><h2>1. Introduction</h2></A> <UL> <LI><A HREF="#bugs">1.1 Errata for FreeBSD 3.1-RELEASE </A> </UL> <A HREF=#build><h2>2. Using the PicoBSD build Procedures</h2></A> <A HREF=#buildoptions><h2>3. Build Options and Extensions</h2></A> <A HREF=#details><h2>4. The Details</h2></A> <UL> <LI><A HREF=#kernel>4.1 Kernel, MFS, and Root File System</A> <LI><A HREF=#bsd>4.2 BSD Kernel Organization</A> <LI><A HREF=#memory>4.3 Memory File Systems</A> <LI><A HREF=#vnode>4.4 VNODE Devices and File Systems</A> <LI><A HREF=#init>4.5 Initializing MFS Root File Systems</A> <LI><A HREF=#compress>4.6 Compressing The Kernel</A> <LI><A HREF=#build_kernel>4.7 Build_kernel, State1, Populate, and Stage2 Scripts</A> <LI><A HREF=#bootseq>4.8 Building the Boot Floppy with Stage3 Script</A> <LI><A HREF=#startup>4.9 Startup and Floppy Files</A> </UL> <A HREF="#multi"><h2>5. Multiple Boot Floppies </h2></A> <UL> <LI><A HREF="#multi_mfs">5.1 Boot and MFS Using Loader</A> <LI><A HREF="#multi_comp">5.2 Boot and Compressed MFS</A> <LI><A HREF="#multi_cdrom">5.3 Boot and CDROM</A> </UL> <A HREF="intro"><h2>1. Introduction</h2></A> <p> The <A HREF=#build>Using the PicoBSD build Procedures</A> section is a guide to using the PicoBSD build facilities. <A HREF=#buildoptions>Build Options and Extensions</A> describes the options used by the <tt>build</tt> script, and the other scripts of the PicoBSD package. The <A HREF=intrinsics.html>Intrinsics</A> document provides an expert level guide to the various scripts and their actions. <A HREF=#details>The Details</A> is a more an expanded form of the <A HREF=intrinsics.html>Intrinsics</A> notes, together with explanations of some of the underlying technology used to make the various PicoBSD components. <A NAME="bugs"><h2>1.1 Errata for FreeBSD 3.1-RELEASE </h2></A> <p> The following errata have been found in the FreeBSD 3.1-RELEASE. <ol> <li> Missing kzip /usr/lib/aout files on FreeBSD 3.1-RELEASE. <p> The necessary library files for the kzip utility are missing on the FreeBSD 3.1-RELEASE. Do the following steps to create and install them: <pre> cd /usr/src/sys/i386/boot/kzipboot make all install </pre> <li> Crunchgen, ppp, and -DRELEASE_CRUNCH <p> Some of the binaries such as ppp will not build correctly unless the RELEASE_CRUNCH environment variable is set to 1. You must also set the -DRELEASE_CRUNCH flag in the ${TYPE}/crunch1/Makfile: <pre> *** net/crunch1/Makefile Sat May 1 16:13:50 1999 --- net/crunch1/Makefile.orig Wed Apr 28 09:22:50 1999 *************** *** 12,18 **** fi @cat crunch.conf|sed -e "s@/usr/src@${SRC}@" >crunch1.conf @crunchgen ./crunch1.conf ! @${MAKE} -f crunch1.mk -DRELEASE_CRUNCH -DNOPAM all \ "CFLAGS=${CFLAGS} -DCRUNCHED_BINARY -DNOSECURE -DNOCRYPT" #2>&1 >/dev/null clean: --- 12,18 ---- fi @cat crunch.conf|sed -e "s@/usr/src@${SRC}@" >crunch1.conf @crunchgen ./crunch1.conf ! @${MAKE} -f crunch1.mk -DNOPAM all \ "CFLAGS=${CFLAGS} -DCRUNCHED_BINARY -DNOSECURE -DNOCRYPT" #2>&1 >/dev/null clean: </pre> </ol> <A NAME="build"><h2>2. Using the PicoBSD build Procedures</h2></A> <ol> <li> Get the file <code>picobsd.tgz</code>. It contains the scripts you'll need. Also, I assume you run quite -current system with full sources installed. <p> NOTE1: beginning with version 0.4, PicoBSD sources are maintained as part of official FreeBSD CVS repository, so if you have recent source tree you can find them in /src/release/picobsd.</p> <p> NOTE2: there were some mysterious interactions between vn(4) driver and 'disklabel auto' in versions earlier than 3.0. There is another set of scripts prepared by <A HREF="mailto:dinesh@alphaque.com">Dinesh Nair </a> which allows to build PicoBSD floppies on a earlier systems.</p> <p> NOTE3: this document reflects the modified build procedures that were developed by Patrick Powell <papowell@astart.com>. These are based on the FreeBSD 3.1-RELEASE distribution. <p> Unpack the archive into your <code>src/release/picobsd</code>. You'll need at least 5MB of free space.</p> </li> <li> Change working directory (<code>cd build</code>) and run the <code>./build</code> script. Select target language, size of MFS and one of pre-canned setups (personal dialup, dialin server or router-like). Details of each setup are contained in dial/, router/, isp/ and net/ directories respectively. You should at least check <code>${TYPE}/config/PICOBSD</code> file to make sure it contains the drivers you want. <p> You can also choose a special type called 'custom'. You'll need to supply the full path to your own custom config tree constructed exactly like one of the standard config directories. Also, you'll probably want to adjust the number of inodes on MFS - see the <code>stage1</code> script and look for <code>INODES=</code>.</p> <p> You can create your own configuration. The recommended method is to start with an existing configuration, such as <code>net</code>, and to copy the files to a your new configuration. For example, to create <code>small</code>, you would do: <pre> cd /usr/src/release/picobsd cp -r net small </pre> </li> <li> There are several directories which contain some sources and config files: <pre> build/ main build directory; you MUST cd here! dial/ config files for dialup setup conf/ kernel config file crunch1/ crunch of system programs mfs.tree/ contains the MFS configuration lang/ contains language-dependent files floppy.tree/ contains the startup floppy hierarchy isp/ config files for dialin server setup ... (as above) net/ config files for router-like setup ... (as above) tinyware/ collection of small system utilities tools/ additional tools them needed during build </pre> <p> There are no <code>/etc/passwd</code> nor <code>/etc/pwd.db</code> files on the "dial" floppy - in case of other types, they are reconstructed from <code>/etc/master.passwd</code> on each startup (and then put on MFS with the rest of <code>/etc</code>). In case of "dial" type floppy, you don't need them at all.</p> <p> NOTE: thanks to the above, the floppy is needed only during startup, and then only if you want to synchronize (possibly changed) MFS /etc with the one on the floppy. It means that you can pull off the floppy from the drive as soon as <code>login:</code> prompt appears. In other words, it is almost equal to read-only floppy.</p> </li> <li> Edit the set of installed programs. <ul> <li> Go to <code>${TYPE}/crunch1</code> directory, and edit it to suit your needs. Keep in mind that floppies aren't made of rubber... :-) </li> <li> There are some patches included in these directories, which are applied during build process to some of the Makefiles in your <code>/usr/src</code>. These patches attempt to decrease the size of some programs by cutting off rarely/unlikely used parts. The patches are reversed when you do a <code>make clean</code> (or <code>build/clean</code> for that matter). <p> NOTE: patches may fail to apply, if your sources are too different from the ones I used. Don't worry: they are so straightforward that you can apply them by hand.</p> </li> <li> In order to have a functioning system you MUST include at least one of <code>init</code>, or <code>oinit</code>, or <code>sysinstall</code> in your <code>crunch.conf</code>. Of course these can be your own programs... But if you install the stock <code>init</code>, you also have to install sh, getty, login etc... to provide for the standard login support. <p> This release of PicoBSD contains a small replacement for init(8), called 'oinit'. You can find it in TinyWare collection. </li> </ul> </li> <li> Make sure that the system you're running has /dev/[r]vn0* entries in /dev directory (if not, you can make them with 'MAKEDEV vn0'), AND that your running kernel has built-in vnode driver (there should be a line in your kernel config file stating 'pseudo-device vn'). </li> <li> You'll need at least 9MB of free disk space, and free /mnt directory. </li> <li> Do a <code>cd build/</code> and fire off the <code>./build</code> script. Select the build parameters or 'n' for 'no change'. If all is well, after some time (like 10-30m) you end up with a 'picobsd.bin' file in this directory. <p> The build script accepts the following options: <pre> build [-v] [-n] [config] -v verbose operation -n do not write floppy config - configuration, must be directory ../config </pre> The -v option will show the execution steps in detail. The -n suppresses writing a floppy. This can be done at a later time by using <code>dd</code> to copy the output <code>picobsd.bin</code> file to the floppy. <pre> dd if=picobsd.bin of=/dev/rfd0 </pre> <p> If there were any errors, please execute the build script using the -v option and try to find what causes this error. Most often this will be one of the following reasons:</p> <ul> <li> <code>crunchgen</code> can't find the source directory for a program 'proggy': <ul> <li> make sure that the source directory for 'proggy' is called 'proggy', otherwise the crunchgen won't find it </li> <li> make sure that the Makefile allows crunchgen to deduce the set of objects to build. You can manually add an OBJS= ... to the program's Makefile. </li> </ul> </li> <li> crunch fails to build. <ul> <li> check your system source tree for stale .depend files and/or objects (*.o) </li> <li> see if the individual programs can be built using original Makefiles. If not, cvsup the correct sources. </li> <li> crunch fails to include all of the object files for a program. See the crunch(8) man page on how to explicitly specify the object files needed. You might also want to try to fix the Makefile for the program if it is a standard system utility. </ul> </li> <li> /: write failed - file system is full <ul> <li> this one is obvious - you wanted to put too many programs on the MFS and/or the target floppy. Or, you really don't have any space left on the root partition.. :-) </li> <li> also, you can check if the MFS size is correctly reported while it's still mounted (right after <code>stage1</code> script ends). </li> </ul> </ul> You can also remove <code>2>&1</code> redirections from Makefiles to see the stderr. </li> </ol> <p>That's all. You're welcome to change and improve these scripts. If you stumble upon something which looks like a good idea to have it here, let me know.</p> <p>If, for some reason, the scripts don't work for you at all, also let me know.</p> <A NAME="buildoptions"><h2>3. Build Options and Extensions</h2></A> <p> The build command is invoked with: <pre> build [-v] [-n] [config] -v verbose operation -n do not write floppy config - configuration, must be directory ../config </pre> <p> The -v option will show the execution steps in detail. The -n suppresses writing a floppy. This can be done at a later time by using <code>dd</code> to copy the output <code>picobsd.bin</code> file to the floppy. <p> The build script's purpose is to set the following environment variables, and then invoke the <code>build_kernel, stage1, populate, stage2, stage3,</code> and optionally <code>install</code> scripts. See <A HREF=intrinsics.html>Intrinsics</A> or the sections below for details about each of these scripts. <ul> <li><code>TYPE</code>=config <br> This is the name of the configuration that will be built. By default, the configuration files will be in <code>../$TYPE</code>, relative to the build directory. The when the <code>custom</code> configuration is used, <code>build</code> will create a symbolic link from <code>../custom</code> to the specified location of the files. <li><code>SRC</code>=path to source files <br>This is the location of the source files for the system configuration. The default is <code>/usr/src</code> <li><code>SIZE</code>=N <br> The number of Kbytes of memory to be used at run time for the MFS (Memory File System). This should be large enough to contain all of the executables files to be placed in MFS. See the discussion later about the Kernel and MFS Generation. <li><code>LANGUAGE</code>en or pl <br> Some of the files put in the MFS during creation have English and Polish versions. The default is <code>en</code> (English). <li><code>INODES</code>=1024 <br>The number of inodes for the MFS. If you want to put a large number of files into the MFS, then you will need to increase the number of inodes. <li><code>PREBUILD_PASSWD</code>=no <br> You can prebuild the /etc/passwd file and put it into the MFS. However, this will take up quite a bit of space. The default is to put a small <code>/etc/master.passwd</code> file into the MFS, and during system initialization to create the <code>/etc/passwd</code> file using the <code>pw_dbgen</code>. This method allows you to store a modified password file on the floppy disk and be able to edit it using normal tools. This is discussed in later sections. </ul> <p> The <tt>build_kernel</tt> script will configure and build a kernel using the files in the <tt>../${TYPE}/conf</tt> directory. The kernel will be in the <tt>/usr/src/sys/i386/compile/PICOBSD-${TYPE}</tt> directory. <p> The <tt>stage1</tt> script will create an MSF file system of <tt>$SIZE</tt> Kbytes and mount it on <tt>/mnt</tt>. The <tt>populate</tt> script will copy files from <tt>../${TYPE}/lang</tt> and generate a <tt>crunch</tt> file using the <tt>../${TYPE}/crunch1</tt> directory and files. These are all put into the MFS generated by <tt>stage1</tt>. <tt>stage2</tt> will put the MFS into the kernel, and finally <tt>stage3</tt> will generate a floppy disk image. The <tt>install</tt> script may then be used to copy the image to an actual floppy disk. <A NAME=kernel><h2>4. The Details</h2></A> <p> This section is a discussion of the various techniques and background technology needed to build a FreeBSD PICOBSD release. It assumes that the reader is familiar with building a FreeBSD system, and has had some experience with system installation as well. <p> For technical details, I recommend studying the reading list available in the FreeBSD FAQ. There are many excellent books and articles there, but it is difficult to know where to start. <A NAME=kernel><h2>4.1 Kernel, MFS, and Root File System</h2></A> <p> One of the more interesting aspects of the picobsd system is how you embed a Memory File System image into a kernel, and how the entire system gets packaged and started. These notes will explain the underlying methods, and will hopefully provide a guideline to persons wanting to use picobsd for some radically different situtations. <p> First, we will review the BSD Kernel boot process, then we will discuss the root file system and how the kernel finds and mounts the root file system. Next we will briefly cover how the Memory File System is implemented in the kernel, and then how you can specify that the root file system is a MFS. Finally, we will explain how the contents of the MFS are initialized at system generation, and how we can then compress a kernel to make it even smaller. <A NAME=bsd><h2>4.2 BSD Kernel Organization</h2></A> <p> Briefly, the BSD kernel image is organized as a series of blocks of bytes in a file. Depending on the type of kernel object format, the location and order differ, but the following blocks are present: <ul> <li>header - tells the size, location, and order of the other blocks in the executable. Also tells the size and location of <code>unitialized</code> data areas, and the <em>staring location</em> or entry point of the code. <li>code - the executable code <li>preinitializaed data - strings and other values that have been statically initialized during system compilation. <li>relocation information - used to update kernel address locations </ul> <p> During system boot, the kernel executable code is loaded into memory, the initialized memory is set up, the uninitialized memory area is zeroed, and the relocation information is scanned and locations in the kernel are set to actual locations of strings or other data structures in memory. The loader will then jump to (or call) the kernel entry point. <p> At this point the kernel is now in charge, and will proceede to execute code to initialize various modules or data structures and hardware devices. This initialization is <em>not</em> the device probing, but a much more low level initialization of devices such as the floating point unit, memory management, and other essential devices. <p> One of the most essential devices is the <em>root file system device</em>. In fact, this is so essential that it is actually specified in the kernel config file used to build the kernel: <pre> config kernel root on fd0a </pre> For details on this, see the <it/Building 4.4BSD Kernels with Config/, part of the FreeBSD system documentation in <tt>/usr/share/doc/smm/04.config</tt>. If you really want to suffer, you can also look at <tt> /usr/src/sys/kern/init_main.c</tt> for some really gory details. <p> By explicitly specifying the root file system device early in the booting process the kernel will be able to read (and write) files from the root device. This allows the kernel to read a kernel configuration file from the root file system which can be used to modify device probing operations. <p> After probing and initializing devices, the kernel will then start scheduling processes for execution. One of the very first processes is the so called <tt>init</tt> process. In FreeBSD 3.1-RELEASE, the kernel will sequentially search for <tt> /sbin/init, /sbin/oinit, /sbin/init.bak, </tt> or <tt> /stand/sysinstall, </tt> and execute the first one it finds. It is the responsibility of this process to carry out the necessary system initialization and set up user logins. By convention, the init process will run the <tt>/etc/rc</tt> script to do initialization, wait for users to log on. See <tt>man init</tt>, or the <tt>/usr/src/sbin/init/init.c</tt> file for the graphic details. <p> You should be aware that init will record login and other information in the following files: <pre> /var/run/utmp The utmp file. /var/log/wtmp The wtmp file. /var/log/lastlog The lastlog file. </pre> The good news is that <tt>init</tt> will not create the files. The bad news is that the files will continue to grow, and may fill up the file system. On very small file system this can be a problem, and you should not create these files or arrange for them to be truncated at appropriate intervals. <A NAME=memory><h2>4.3 Memory File Systems</h2></A> <p> A Unix file system consists of a <em>device</em> corresponding to hardware where the information is stored, a <em>device driver</em> which provides the necessary kernel level operations to read and write data from the device, and a <tt>mount service</tt> which provides the interface between the kernel's file system primitives (read, write, open, etc) and the device driver. The mount service can also translate one file system format such as MSDOS into sensible UNIX files. <p> The Memory File System has a rather simple device and device driver: the device is a block of memory. The device driver simply copies information to and from these memory areas. <p> This memory area can in two places: <ol> <li> Process image, which can be swapped out, size limited by swap space <li> Kernel memory, size limited by physical memory </ol> <p> At this point, I suggest you look at the MFS man page (<tt>man mfs</tt>) and <em>A Pageable Memory Based Filesystem</em> in <tt> /usr/share/doc/papers/memfs.ascii.gz</tt> for details of how this is implemented. Effectively, you run the <tt>mfs_mount</tt> command all that happens is a process will do a <tt/malloc/ of the amount of memory requested, and the device driver then does reads and writes to the process image. <p> The MFS which uses Kernel Memory is the one which we are interested in. We first need to generate a kernel which will support MFS and allow us to use the MFS as a root device. We do this with the configuration options: <pre> options MFS #Memory File System options MFS_ROOT #MFS usable as root device options MFS_ROOT_SIZE=10 #Size in Kbytes </pre> <p> The first option, MFS, simply adds the MFS kernel modules to the kernel. The second option will define a <em>initialized</em> <tt>mfs_root</tt> character array of size <tt>MFS_ROOT_SIZE</tt> K bytes, and force the kernel to use this array as the root file system. <p> Here is the declaration of the <tt>mfs_root</tt> variable in <tt>/usr/src/sys/ufs/mfs/mfs_vfsops.c</tt>: <pre> static u_char mfs_root[MFS_ROOT_SIZE*1024] = "MFS Filesystem goes here"; static u_char end_mfs_root[] __unused = "MFS Filesystem had better STOP here"; </pre> <p> During the system initialization, the kernel will look at the <tt>mfs_root</tt> variable, and expect it to have the same format as a UFS file system. If it does, then it will treat this device as the root device, and ignore any root device specified by the <tt>config</tt> specification. <p> We will explore how we can construct a file system and place it in the <tt>mfs_root</tt> array in the next couple of sections. <A NAME=vnode><h2>4.4 VNODE Devices and File Systems</h2></A> <p> In a previous section, we discussed how the MFS file system allows blocks of memory (even in a user process) to be used as a file system. Well, files are simply blocks of memory in some sense. Can we create a device that will allow us to access a file as a <em>device</em>? <p> The <em>vnode</em> device driver does exactly this. When we run the <em>vnodeconfig</em> program, this establishes a link between a file and a vnode device. The vnode device driver then tranlates low level kernel block io reads and writes in file lseeks and file read and write operations. Lets see how we can construct a file that we can uses as a device: <pre> dd of=fs.PICOBSD if=/dev/zero count=2500 bs=1k 2> /dev/null vnconfig -s labels -c /dev/rvn0 fs.PICOBSD 2>/dev/null </pre> <p> The dd command will create a 2500 Kbyte file of all zeros, and then the <tt>vnconfig</tt> command will link the file to the <tt>/dev/rvn0</tt> and <tt>/dev/vn0</tt> devices. You can use <tt>/dev/rnv0</tt> to access the file contents: <pre> dd if=/dev/rnv0 bs=1 count=4 seek=32 </pre> But we can also <em>format</em> the device ... I mean file so that its contents have the structure of a hard drive. In the FreeBSD environment we use the <em>disk label</em> utility to write a block of data at a specific offset from the start of the disk... I mean file, that provides information about the number of sectors, tracks, and cylinders on the device, and installs any boot blocks if requred. This is done using the <tt>disklabel</tt> command as follows: <pre> disklabel -rw vn0 auto </pre> <p> The <tt>auto</tt> option will simply query the VNODE driver code for the disk information and will copy this to the label area. See <tt>man disklabel</tt> for details about this operation. <p> The file contents now are an image of a generic disk drive, and we can now create a file system on it using the <tt>newfs</tt> command: <pre> INODES=4096 newfs -i $INODES -m 0 -p 0 -o space /dev/rvn0c 2>&1 >/dev/null mount /dev/vn0c /mnt </pre> <p> The number of <tt>inodes</tt> that are created on the disk determines the maximum number of files that can be stored on the disk. The <tt>-i 4096</tt> parameter specifies that we create one inode for each 32K bytes. One a 2048Kbyte file system, this would be 512 inodes, or a maximum of 512 files. <A NAME=init><h2>4.5 Initializing MFS Root File Systems</h2></A> <p> By using <tt> dd, vnconfig, disklabel, </tt> and <tt>newfs</tt>, we can create a file <tt>fs.PICOBSD</tt> whose contents are exactly the same as a copy of a real disk system. We can mount this device and then copy files to it: <pre> mkdir /mnt/etc cp /etc/rc /mnt/etc/rc cp /etc/init /mnt/etc/rc ... umount /mnt fsck -p /dev/rvn0c vnconfig -u /dev/rvn0 </pre> <p> At this point, we will have a file, <tt>fs.PICOBSD</tt>, whose contents are exactly the same a disk based file system, and which we could use as a root file system if it was on a physical device. <p> Now we do a really tricky thing: we are going to copy this file system image into the MFS area in the kernel. We find the string <tt>"MFS Filesystem goes here"</tt> in the kernel image, and we brutally copy the <tt>fs.PICOBSD</tt> file over top of it. <p> The <tt>write_mfs_in_kernel</tt> utility will do exactly this; see <tt>/usr/src/release/picobsd/tools/write_mfs_in_kernel </tt> for details. We use: <pre> write_mfs_in_kernel kernel fs.PICOBSD </pre> <p> If we were to load this kernel into memory and start it executing, it would examine the contents of the <tt>mfs_root</tt>, discover that it is a file system image, and then the MFS file system image as its root disk. <A NAME=compress><h2>4.6 Compressing The Kernel</h2></A> <p> In the previous steps we outline how we would make a kernel with a MFS root file system, and how we would initialize the file system. However, there is a small problem with this kernel - it is extremely large. We have a least 2500 K bytes of initialized data (the MFS image). <p> We can get around this by compressing the kernel using (say) gzip (Lempel-Ziv algorithm), or the even more efficient bzip (block-sorting algorithm). One of the more pleasant things about these algorithms is that they compress long strings of zeros (0) with excellent efficiency. Here are some sample numbers <pre> Filesystem 1K-blocks Used Avail Capacity iused ifree %iused /dev/vn0c 2387 1300 1087 54% 285 353 45% Raw Stripped Compressed Kernel (2500K MFS) 4102K 3840K 1258K Kernel (3500K MFS) 5126K 4864K 1260K </pre> As you can see, there is approximately a 2k increase in compressed kernel size for a 1000K increase in MFS size. <A NAME=build_kernel><h2>4.7 Build_kernel, Stage1, Populate, and Stage2 Scripts</h2></A> <p> The <tt>build</tt> script will first call the <tt>build_kernel</tt> script to build a kernel with the MFS and MFS_ROOT options and MFS_ROOT_SIZE set to the MFS file system size. <p> Next, the <tt>stage1</tt> script generates the <tt>fs.PICOBSD</tt> file which will be used for the Memory File System. This is done using the commands: <pre> umount /dev/vn0 2> /dev/null || true umount /mnt 2> /dev/null || true vnconfig -u /dev/rvn0 2> /dev/null || true # generate a file of $SIZE K of zeros dd of=fs.PICOBSD if=/dev/zero count=${SIZE} bs=1k 2> /dev/null vnconfig -s labels -c /dev/rvn0 fs.PICOBSD 2>/dev/null # we autolabel this disklabel -rw vn0 auto n= if [ -n "$INODES" ] ; then n="-i $INODES"; fi newfs $n -m 0 -p 0 -o space /dev/rvn0c 2>&1 >/dev/null mount /dev/vn0c /mnt </pre> <p> We now have a file system mounted on <tt>/tmp</tt> which we will now fill with the files for the MFS. The <tt>populate</tt> will effectively generate a root file system by creating the <tt> /dev, /etc, /usr, /sbin, </tt> and other directories, creating the device files in <tt>/dev</tt>, and copying other files in place. <p> The <tt>populate</tt> script also generates a <em>crunch</em> executable, which is a single file that has the executables for a large number of commands. This reduces the number of duplicate statically linked executables that are needed for a small file system. <p> After the MFS is created and populated, the <tt>stage2</tt> script will embed the MFS into the kernel, and strip and compress the kernel. <pre> umount /mnt 2>&1 >/dev/null fsck -p /dev/rvn0c vnconfig -u /dev/rvn0 2>&1 >/dev/null if [ ! -f ../tools/write_mfs_in_kernel/wmik ]; then (cd ../tools/write_mfs_in_kernel; make) fi ../tools/write_mfs_in_kernel/wmik kernel fs.PICOBSD strip kernel strip --remove-section=.note --remove-section=.comment kernel gzip -9 -n -f kernel </pre> <p> First, we unmount the <tt>fs.PICOBSD</tt> file, and run <tt>fsck</tt> to ensure that it is in a consistent state and mark the file system as <em>clean</em> so the kernel will not try to run <tt>fsck</tt> during system startup. We also strip the kernel to remove symbol tables and contents. This reduces the kernel size substantially, but means that we will be unable to use some utilities that require a symbol table. We then use the <tt>write_mfs_in_kernel</tt> command to copy the MFS to the kernel, and then use gzip to compress the kernel. <p> We are now ready to put all of this onto a floppy disk. But first, let us review how we boot from a floppy disk. <A NAME=bootseq><h2> 4.8 Building the Boot Floppy with Stage3 Script</h2></A> <p> The <tt>stage3</tt> we creates a floppy image file and then mounts it on /mnt using the following <tt> dd, vnconfig, newfs, </tt> and <tt> mount </tt> commands: <pre> umount /dev/vn0 2> /dev/null || true umount /mnt 2> /dev/null || true vnconfig -u /dev/rvn0 2> /dev/null || true dd of=picobsd.bin if=/dev/zero count=1440 bs=1k 2> /dev/null vnconfig -c /dev/rvn0 picobsd.bin 2>/dev/null disklabel -Brw /dev/rvn0 fd1440 2>&1 >/dev/null newfs -i 4096 -m 0 -p 0 -o space /dev/rvn0c 2>&1 >/dev/null mount /dev/vn0c /mnt </pre> <p> The <tt>dd</tt> command creates a 1440Kbyte file which will be our floppy disk image. The <tt>vnconfig</tt> then attaches it to the <tt>/dev/rvn0</tt> and <tt>/dev/vn0</tt> VNODE device. <p> The <tt>disklabel</tt> command is where a lot of subtle things happen that are not explicity visible. The <tt>-B</tt> option causes the <tt>/boot/boot1</tt> and <tt>/boot/boot2</tt> files to be written to the floppy disk, and installed as boot blocks for the device. The <tt>fd1440</tt> will cause the disk image to be labelled as a 1440K floppy disk. Finally, we mount the floppy image onto <tt>/mnt</tt> in preparation for copying files to it. <p> The inquisitive readers who examine the <tt>/boot</tt> directory will discover there is also a <tt>/boot/boot0</tt> file. The <tt>/boot/boot0</tt> is the boot file used on <it>multi-system</it> hard drives, and allows users to select which of multiple systems they wish to boot. This boot file is installed in the Master Boot Record (MBR) which is only available on drives that support a MSDOS partition structure, such as hard drives and some removable media (Zip) drives. Since we are using a floppy disk, we do not need to worry about <tt>boot0</tt>. <p> We copy the compressed kernel to the floppy image: <pre> cp kernel.gz /mnt/kernel.gz </pre> <p> At this point, you can copy the floppy image to an actual floppy disk and then mount the floppy using the following commands: <pre> # for writes to all devices including VNODE devices sync dd if=fs.PICOBSD of=/dev/rfd0 mkdir /tmp/mnt mount /dev/fd0 /tmp/mnt ls /tmp/mnt </pre> The <tt>ls</tt> command will show that the floppy disk has the <tt>kernel.gz</tt>. If you feel adventurous, you can try to boot the floppy. If you do, you will be suprised to find that the boot code reports that it cannot find a <tt>/kernel</tt> file. Even if you <em>rename</em> the <tt>kernel.gz</tt> to <tt>kernel</tt>, then the boot code will still give an error. <p> What is happening? The <tt>boot1</tt> and <tt>boot2</tt> loaders expect to load reqular, non-compressed kernels into memory and execute them. They do not have any uncompression facilities as they are very small and simple. <p> One way to handle this is to use the <tt>kzip</tt> utility, which compresses a kernel, then adds code to uncompress the kernel and then jump to the kernel entry point. <p> Rather than use this, we will add a <em>level3</em> bootstrap loader that not only is smart enough to not only uncompress the kernel, but also can pass in user specified configuration values and perform other configuration functions. <p> Here is the final boot sequence we will use: <ol> <li> boot1 on floppy sector 0/0/1 loaded into memory and executed by BIOS <li> boot2 on UNIX partition loaded into memory and executed by boot1 (uses /boot.config for information) <li> /boot/loader loaded memory and executed by boot2 (uses /boot/loader.rc for information) <li> /kernel.kz decompressed and loaded into memory, modified by /boot/loader, and executed. (uses /kernel.config for information) </ol> <p> The <tt>boot1</tt> code (<tt>/boot/boot1</tt>) was placed on the first sector of the floppy by the <tt>disklabel</tt> program. It also labelled the disk and wrote the <tt>boot2</tt> code (<tt>/boot/boot2</tt>) into a standard sector location. Boot1 has just enough code to load Boot2 using the PC BIOS facilities, and is limited to loading it from a standard location in a file system partition. Boot1 finds UNIX partition, copies boot2, to memory and then jumps to the boot2 entry point. <p> Boot2 is about 8Kbytes of code and data, and can read a Unix UFS file systems using BIOS calls. For details on its capabilities, see <tt>man boot</tt> for details. There are several hardwired files and configuration entries in boot4. Boot2 will examine the partition that it was loaded from and make sure it has a UNIX UFS file system or a file system it can manage. It will then try to open <tt>/boot.config</tt>, read <em>a single line from the file</em>, and interpret it in the same way as a user specified boot optons. It will then pause for a few seconds to allow a user to entry new or modified values by pressing a key during the boot process and entering them at the prompt. <p> The PICOBSD setup uses its capabilities by creating a <tt>/boot.config</tt> on the floppy disk image and initialing it with: <pre> /boot/loader </pre> This will cause <tt>boot2</tt> to find and load file <tt>/boot/loader</tt> into memory and execute it as a kernel. <p> What is this all about? The answer is simple: we want to be able to configure the devices and we need a loader that is smart enough to uncompress our kernel. The boot3 loader (just called <tt>loader</tt> now) has these and a tremendouse number of other capabilities, and it just a trifle large (131Kbytes). We use the <tt>kzip</tt> (kernel zip) facility to compress it and attach a bit of code to decompress and then run the compressed code, and then copy the compressed file to the <tt>/boot/loader</tt> file on the floppy: <pre> # we make a compressed loader mkdir /mnt/boot cp /boot/loader . mkdir /mnt/boot cp /boot/loader loader kzip -v loader mv loader.kz /mnt/boot/loader rm -f loader loader.o echo "/boot/loader" >/mnt/boot.config </pre> <p> The boot2 loader copies the file <tt>/boot/loader</tt> into memory now executes it. The <tt>/boot/loader</tt>will now uncompress itself and can read commands from the console or the following file (whose location is not very well documented in the man pages): <pre> /boot/loader.rc </pre> <p> (Aside: <tt>/boot/loader.rc</tt> used to be <tt>/boot/boot.conf</tt> in earlier FreeBSD releases, and this name is is pretty close to boot.config (see above). I wonder how many people this has confused?.) <p> If there is not file or there are no commands in the file then the <tt>/boot/loader</tt> will try to locate <tt>/kernel, /kernel.gz, /kernel.old, /kernel.gz.old, </tt> in turn and will load the first one found into memory and execute it. <p> The PICOBSD <tt>stage3</tt> script initializes <tt>/boot/loader.rc</tt> on the floppy disk with the following commands: <pre> load /kernel load -t userconfig_script /kernel.config boot -P </pre> <p> If you are interested in the capabilities of <tt>loader</tt>, see <tt>/usr/src/boot/common/help.common</tt> or <tt>/boot/loader.help</tt> for a command summary. <p> Now let us see what happens when <tt>/boot/loader</tt> carries out the above commands. The <tt>load /kernel</tt> causes it to search for <tt>/kernel</tt> and <tt>/kernel.gz</tt>; it will find <tt>/kernel.gz</tt> and uncompress it as it is loaded into memory. <p> The <tt>load -t userconfig_script /kernel.config</tt> will cause the loader to copy the contents of <tt>/kernel.config</tt> into memory, and label it as the <tt>userconfig_script</tt> module. When the kernel is started, the <tt>userconfig</tt> module in the kernel (see <tt>/usr/src/sys/i386/i386/userconfig.c</tt> for the nastier details) will check to see if the <tt>userconfig_script</tt> module has been loaded, and will then read device configuration settings from it. The commands can have the form: <pre> cmd usage example di disable dev di de0 dr drq dev # dr 4 ei eisa # (EISA slots)ei 4 en enable dev en ep0 ex exit (quit) ex f flags dev mask f de0 0xff h help h intro intro screen intro iom iomem dev addr iom ed0 0xdf0000 ios iosize dev size iosize ed0 0x1000 ir irq dev # irq ed0 5 l ls, list l pn pnp (scan) pnp po port dev addr port ed0 0x300 res reset CPU res q quit q v visual mode v </pre> <p>If you generate a stock kernel for a large number of systems, you may discover that some systems have different hardware IO addresses than those in the kernel. The need for port (io) address and interrupt modification is used only with ISA and not with PCI bus cards. <p> The <tt>boot -P</tt> causes the <tt>/boot/loader</tt> to jump to the loaded kernel entry point, and pass it a <tt>-P</tt> (use serial console if no keyboard) flag. See <tt>man boot</tt> for details of the flags and their actions. <p> Finally, kernel execution is started. The kernel does its initialization, and the kernel <tt>userconfig</tt> kernel module is initialized. The <tt>userconfig_script</tt> value set by <tt>/boot/loader</tt> causes it to reads the <tt>/kernel.config</tt> file, and if we have generated our kernel with the INTRO_USERCONFIG option, the kernel will go into VISUAL <tt>userconfig</tt> operation. <p> To stop the loader from going into VISUAL <tt>userconfig</tt> operation, you must to modify the PICOBSD kernel configuration file. Comment out the following line: <pre> options INTRO_USERCONFIG #imply -c and parse </pre> and regenerate the kernel. <p> Pleae be aware that the MFS in the kernel is not used until <em>after</em> the devices have been probed. Thus, the <tt>boot.config</tt> and <tt>kernel.config</tt> files will be read from the floppy disk file system. <p> Here are the commands from <tt>script3</tt> used to set up the floppy disk for booting: <pre> mkdir /mnt/boot cp /boot/loader . kzip -v loader mv loader.kz /mnt/boot/loader rm -f loader loader.o # set up the /boot.config file to cause boot2 to load /boot/loader echo "/boot/loader" >/mnt/boot.config # set up the /boot/loader.rc to cause the /boot/loader to # load in the kernel and set options cat >/mnt/boot/loader.rc <<EOF load /kernel load -t userconfig_script /kernel.config boot -P EOF </pre> <A NAME=startup><h2>4.9 Startup and Floppy Files</h2></A> <p> Once the kernel has been loaded into memory, and execution has started, it will run the first of <tt> /sbin/init, /sbin/oinit, /sbin/init.bak, /sbin/init.bak, </tt> or <tt> /stand/sysinstall </tt> that it finds <em>on the root file system</em> which is actually the MFS, due to the <tt>MFS_ROOT</tt> kernel configuration option. If the MFS file system contained all of the files that we would ever need, or we would not need to modify any configuration options, then we are finished with setting up our system. <p> On the other hand, we might discover that there are some configuration settings such as network IP addresses, hard drives to be mounted, or a <tt>/var</tt> MFS file system to be created and mounted. To allow our system to be easily configured, we will put a set of files on the floppy disk which are then copied to the MFS file system as part of the boot procedure. This is managed by following items in the PICOBSD installation. <ol> <li><tt>${TYPE}/floppy.tree/*</tt> <br> These files are copied to floppy disk during the PICOBSD installation process. The default is a <tt>boot.config</tt> and <tt>kernel.config</tt> to be used during the boot process, and a <tt>/etc</tt> directory containing files which will overwrite the MFS <tt>/etc</tt> files. <li> <tt>${TYPE}/lang/mfs.rc.${LANG}</tt> <br>This file is copied to the MFS <tt>/etc/rc</tt> file, and will be executed as part of the system startup procedure. </ol> Here is the contents of the <tt>net/lang/mfs.rc.en</tt> file: <pre> mount -o rdonly /dev/fd0a /start_floppy cd /start_floppy/etc cp -Rp . /etc/ cd /etc pwd_mkdb -p ./master.passwd umount /start_floppy echo "Ok. (Now you may remove floppy if you like)" echo "" . rc exit 0 </pre> <p>Let us see what this does. First, it mounts the floppy disk on the <tt>/start_floppy</tt> directory, and then copies all the files in the floppy <tt>/etc</tt> directory to the MFS <tt>/etc</tt> directory. Then it unmounts the floppy and proceeds to execute the <tt>/etc/rc</tt> file again. This may appear to be an endless loop, but if we have an <tt>/etc/rc</tt> file on the floppy, this will overwrite the MFS <tt>/etc/rc</tt> file, and we will execute it instead of the MFS file. <p> The <tt>${TYPE}/lang/rc.en</tt> file is copied to the floppy <tt>/etc/rc</tt> file during setup. Here is a sample to show the various things that can be done. <pre> #!/bin/sh ############################################ ### Special setup for one floppy PICOBSD ### ### THIS IS NOT THE NORMAL /etc/rc !!!!! ### ############################################ if [ -f /etc/rc.conf ]; then . /etc/rc.conf fi mount -a -t nonfs swapon -a if [ "x$swapfile" != "xNO" -a -w "$swapfile" -a -b /dev/vn0b ]; then echo "Adding $swapfile as additional swap." vnconfig /dev/vn0b $swapfile && swapon /dev/vn0b fi rm -f /var/run/* # configure serial devices if [ -f /etc/rc.serial ]; then . /etc/rc.serial fi # start up the initial network configuration. if [ -f /etc/rc.network ]; then . /etc/rc.network network_pass1 fi mount -a -t nfs chmod 666 /dev/tty[pqrsPQRS]* #(cd /var/run && { cp /dev/null utmp; chmod 644 utmp; }) if [ -n "$network_pass1_done" ]; then network_pass2 fi if [ -n "$network_pass2_done" ]; then network_pass3 fi if [ "X${inetd_enable}" = X"YES" ]; then echo "Starting inetd."; inetd ${inetd_flags} fi # make device database dev_mkdb exit 0 </pre> <p> This looks a lot more like a regular <tt>rc</tt> startup file. However, this is very stripped down, and makes a lot of assumptions about the environment it is running in. First, the <tt>/etc/rc.conf</tt> file is read, and is used to set various options that are used in the script. The <tt>mount -a -t nonfs</tt> will mount all of the non-NFS files that are listed in the <tt>/etc/fstab</tt> file. If you have a CDROM or hard drive that you want to mount, you can add this the the <tt>/etc/fstab</tt> file. The <tt>swapon -a</tt> command will set any swap that is specified in <tt>/etc/fstab</tt>. <p> The next entry shows how you can set up a swap file using the VNODE file system. This is useful if the drive that you are mounting is a single partition and you do not want to have a swap partition. One example of this is when you have a MSDOS file system that you mount but still want to have a swap file. This is rather ugly and nasty, but in small systems which need to boot on multiple platforms can prove to be very useful. <p>The <tt> rm -f /var/run/*</tt> command is very standard and makes sure that there are no entries in the <tt>/var/run</tt> directory. If this is MFS, then there will not be any, but you might have mounted <tt>/var</tt> in a previous step, and this will clean it out. <p> After setting up swap, we proceed to start up networking and then mount any NFS file systems specified in <tt>/etc/fstab</tt>. Finally, we start up the various servers that we want to run on our system. <A NAME="multi"><h2>5. Multiple Boot Floppies </h2></A> <p> One of the problems that you will sooner or later encounter is that you simply cannot get all of your code onto a single floppy disk. The following sections outline some procedures you can use to solve this problem. <A NAME="multi_mfs"><h2>5.1 Boot and MFS Using Loader</h2></A> <p>One of the most common solutions is to have two or more floppies. The first one contains the kernel and a very small amount of <em>glue</em> files, while the other contains the actual MFS file system. If you check the <tt>/boot/loader.help</tt> file in the FreeBSD 3.1-RELEASE, you will discover the following interesting commands: <pre> echo [-n] [<message>] Emits <message>, with no trailing newline if -n is specified. This is most useful in conjunction with scripts and the '@' line prefix. Variables are substituted by prefixing them with $, eg. echo Current device is $currdev will print the current device. read [-t <value>] [-p <prompt>] [<variable name>] The read command reads a line of input from the terminal. If the -t argument is specified, it will return nothing if no input has been received after <value> seconds. (Any keypress will cancel the timeout). If -p is specified, <prompt> is printed before reading input. No newline is emitted after the prompt. If a variable name is supplied, the variable is set to the value read, less any terminating newline. load [-t <type>] <filename> Loads the module contained in <filename> into memory. If no other modules are loaded, <filename> must be a kernel or the command will fail. If -t is specified, the module is loaded as raw data of <type>, for later use by the kernel or other modules. <type> may be any string. </pre> <p> Now suppose that we were to place the following commands into the <tt>/boot/loader.rc</tt> file: <pre> load /kernel echo Please insert MFS root floppy and press enter: read load -t mfs_root /mfsroot boot </pre> <p> The <tt>load</tt> command will cause the loader to load (and uncompress) the <tt>/kernel</tt> or <tt>/kernel.gz</tt> file. The <tt>echo</tt> and <tt>read</tt> print a line on the console and then wait for the user to put a new floppy in the floppy drive. The <tt>load -t mfs_root /mfsroot</tt> command is very interesting. It will cause the loader to copy the contents of the file <tt>/mfsroot</tt> into the kernel module <tt>mfs_root</tt>. The <tt>mfs_root</tt> is where the kernel expects to find the MFS image. <p> Thus, we can simply put our MFS file system onto a floppy disk in the file <tt>/mfsroot</tt> and we can now load this. <p>A basic limitation of this method is that your MFS must fit in a single file, and cannot be compressed. However, as we will see in the next section, we can easily get around this problem. <A NAME="multi_comp"><h2>5.2 Boot and Compressed MFS</h2></A> <p>As we saw in previous sections, the root file system must contain an <tt>init</tt> program which is the first process started by the kernel. We can use the <init>process to perform the following actions: <ol> <li>start a shell script which will <ol> <li> ask the user to mount a floppy NNN <li> mount the floppy <li> check to see that there is a file, say FNNN, on the floppy. This will contain compressed files. <li> Uncompress the file FNNN from the floppy <li> After all the files have been extracted from the floppy, runs a <it>cleanup</it> script that would change permissions, create database files, or concatenate files that needed to be split to put them on a floppy. </ol> <li> runs the <tt>/etc/rc</tt> script </ol> <p> This is simply an adaptation of a common method used to distributes software on floppy disk media. <A NAME="multi_cdrom"><h2>5.3 Boot and CDROM</h2></A> <p> If you have a CDROM reader on your system, then it is possible to simply mount a CDROM and extract the files from the CDROM. However, these files are still placed in memory and on systems with limited amounts of memory and large numbers of files needed this may not be desireable. <p> The <b>union</b> file system mount facility can be used to solve this problem. A union mount allows you to overlay two directory structures. For example, suppose that I had directory <tt>/my/usr</tt> which contained a set of files that I wanted to use in place of the standard <tt>/usr</tt> files. Due to hardwired paths in various systems utilities, I could simply not change a PATH environment variable. I would normally be forced to copy all of the files from <tt>/my/usr</tt> to <tt>/usr</tt>, and then copy the originals back in place. This can be very time consuming and is fraught with danger, especially if extra files are left in place. The <tt>mount -b </tt> option will invert the search order, so that the original file system is search first, then the union mount. <p> Consider the following mount command: <pre> mount -t union /my/usr /usr </pre> The <tt>/usr</tt> is the common or <it>uniondir</tt>. The mount command causes the <tt>/my/usr</tt> directory to appear at <tt>/usr</tt>; when a file is searched for, the <tt>/my/usr</tt> directory contents are searched first, and if the file is not found then the original <tt>/usr</tt> is searched. <p> Now suppose that we have a CDROM that has all of the files that we would like to use. We can mount the cdrom and then union mount it as follows: <pre> mount -t cd9660 /dev/wcd0a /mnt_cd mount -t union /mnt_cd / </pre> Now when we access a file, we will get the file from the CDROM first. <p> One of the problems with this method is that every time we search for a directory, then a shadow directory is created in the original MFS file system as well. We can rapidly run out of INODES unless we are careful, as our MFS file system is usually pretty small due to space limitations of the kernel. A way around this is to simply create a true Memory File System and mount this. We then can mount and copy files to this MFS file system. This is most effectively done during the <tt>/etc/rc</tt> initialization. <pre> # /etc/rc mount -t cd9660 /dev/wcd0 /cdrom # allow 1024 blocks of 512 bytes or 512K, lots of inodes for shadowing mount_mfs -i 2048 -s 1024 -T /dev/null /usr # allow 102400 blocks of 512 bytes or 51200K mount_mfs -s 102400 -T /dev/null /var # want to have /usr writable so we reverse order mount -b -t union /cdrom/usr /usr </pre> <p> The good news is that this is an <b>excellent</b> way to provide small MFS based systesm. The bad news is that as of FreeBSD 3.1-RELEASE, the union file system has severe problems and is not yet fully supported. <hr> <i>Last modified: Fri Apr 30 18:14:55 PDT 1999 by Patrick Powell <papowell@astart.com> <br> <i>Generated: @DATE@ <p><A HREF="mailto:papowell@astart.com"><papowell@astart.com></a></i></p> <p><A HREF="mailto:abial@freebsd.org"><abial@freebsd.org></a></i></p> </body> </html> --------------EBAC1F2D12499441013FE952-- To Unsubscribe: send mail to majordomo@FreeBSD.org with "unsubscribe freebsd-small" in the body of the message
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?36E9B1B5.3AD7E06C>