Chapter 2. A Simple Prototype

Table of Contents

1. Analysis
2. Design
2.1. Simplification
2.2. Boot Disk
2.3. Root Disk
2.4. CPU Compatibility
3. Construction
3.1. Prepare the boot disk media
3.2. Build the GRUB bootloader
3.3. Copy the bootloader files to diskette
3.4. Finish bootloader installation
3.5. Build the Linux kernel
3.6. Copy the kernel to diskette
3.7. Unmount the boot disk
3.8. Prepare the root disk media
3.9. Build BASH
3.10. Copy BASH to the root disk
3.11. Create device files that BASH needs
3.12. Unmount the root disk
4. Implementation
4.1. System startup
4.2. Testing what works
4.3. Noting what does not work
4.4. System shutdown

1. Analysis

Since this is the first phase of the project it will be kept very simple. The goal here is not to create the ultimate GNU/Linux system on the first try. Instead, we will be building a very minimal, working system to be used as a building block in subsequent phases of the project. Keeping this in mind, we can list a few goals for phase one.

  • Keep it simple to avoid stressing out.

  • Build something that works for instant gratification.

  • Make something that it is useful in later phases of the project.

2. Design

2.1. Simplification

Take a moment to skim through the Bootdisk-HOWTO or the From-PowerUp-to-BASH-Prompt-HOWTO. These HOWTO documents can be found online at http://www.tldp.org/docs.html#howto. Both documents offer an excellent view of what it takes to get a GNU/Linux system up and running. There is also a lot of information to digest. Remember that one of our goals is, "keep it simple to avoid stressing out," so we want to ignore everything but the absolutely critical pieces of a boot / root diskset.

Basically it boils down to the following required items:

  • A boot loader

  • The Linux kernel

  • A shell

  • Some /dev files

We don't even need an init daemon. The kernel can be told to run the shell directly by passing it an option through the boot loader.

For easy construction we will build a two-disk boot / root set rather than trying to get everything onto a single diskette. The boot loader and kernel will go on the boot disk and the shell will reside on the root disk.

2.2. Boot Disk

For the boot disk we simply need to install the GRUB bootloader and a Linux kernel. We will need to use a kernel that does not require modules for the hardware we need to access. Mainly, it should have compiled-in support for the floppy drive, ram disk, second extended filesystem, proc filesystem, ELF binaries, and a text-based console. If such a kernel is not available, it will need to be built from source code. Kwan Lowe's Kernel Rebuild Guide is a good reference for this task, however we can ignore the sections that deal with modules and the initial ramdisk.

2.3. Root Disk

For the root disk we will need a floppy that has been prepared with a filesystem. We will also need a BASH shell that is statically-linked so we can avoid the additional complexities of shared libraries. The configure program in the BASH source code recognizes the --enable-static-link option for this feature. We will also be using the --enable-minimal-config option to keep the BASH binary down to a manageable size. Additional requirements for the root disk are a /dev directory and a device file for the console. The console device is required for BASH to be able to communicate with the keyboard and video display.

2.4. CPU Compatibility

There is one other, less obvious requirement to keep in mind and that is CPU compatibility. Each generation of CPU features a more complex architecture than its predecessor. Late generation chips have additional registers and instructions when compared to an older 486 or 386. So a kernel optimized for a new, fast 6x86 machine will not run on an older box. (See the README file in the Linux kernel source code for details.) A BASH shell built for a 6x86 will probably not run on an older processor either. To avoid this problem, we can choose the 386 as a lowest common denominator CPU and build all the code for that architecture.

3. Construction

In this section, we will be building the actual boot disk and root disk floppies. Lines preceded by bash# indicate a shell command and lines starting with grub> indicate a command typed within the grub shell.

3.1. Prepare the boot disk media

Insert a blank diskette labeled "boot disk".

Note

It may be necessary to erase the "blank" diskette if it comes factory pre-formatted for another, non-Linux operating system. This can be done using the command dd if=/dev/zero of=/dev/fd0 bs=1k count=1440

bash# mke2fs -m0 /dev/fd0
bash# mount /dev/fd0 /mnt

3.2. Build the GRUB bootloader

Get the GRUB source code from ftp://alpha.gnu.org/gnu/grub/ and unpack it into the /usr/src directory.

Configure and build the GRUB source code for an i386 processor by using the following commands:

bash# cd /usr/src/grub-0.95
bash# export CC="gcc -mcpu=i386"
bash# ./configure --host=i386-pc-linux-gnu --without-curses
bash# make

3.3. Copy the bootloader files to diskette

Normally, after compiling source code, one would use the command make install to copy the finished files to their proper destinations in the filesystem. However, using make install does not work well with small media like the floppy disks we are using. The problem is that there are many files in a package besides the actual binaries that get the job done. For example, there are often man or info pages that provide documentation. These extra files can take up more space than we can spare on the diskette. We can work around this limitation by copying essential files manually rather than using make install.

For GRUB to boot we will need to copy the stage1 and stage2 bootloader files to the /boot/grub directory on the boot floppy.

bash# mkdir -p /mnt/boot/grub
bash# cp /usr/src/grub-0.95/stage1/stage1 /mnt/boot/grub
bash# cp /usr/src/grub-0.95/stage2/stage2 /mnt/boot/grub

3.4. Finish bootloader installation

Once the bootloader's files are copied to the boot disk we can enter the grub shell to finish the installation.

bash# /usr/src/grub-0.95/grub/grub
grub> root (fd0)
grub> setup (fd0)
grub> quit

3.5. Build the Linux kernel

The steps for building the kernel were tested using Linux kernel version 2.4.26 and should work any 2.4.x or 2.6.x kernel. The latest version of the kernel source code may be downloaded from http://www.kernel.org/ or one of its mirrors.

Note

The instructions below are very brief and are intended for someone who has previous experience building custom kernels. A more detailed explanation of the kernel building process can be found in the Kernel Rebuild Guide by Kwan Lowe.

bash# cd /usr/src/linux
bash# make menuconfig

Be sure to configure support for the following:

  • 386 processor

  • Console on virtual terminal (2.4.x kernels only)

  • ELF binaries

  • Floppy disk

  • proc filesystem

  • RAM disk with a default size of 4096K

  • Second extended (ext2) filesystem

  • VGA console

bash# make dep
bash# make clean
bash# make bzImage

3.6. Copy the kernel to diskette

bash# cp /usr/src/linux/arch/i386/boot/bzImage /mnt/boot/vmlinuz

3.7. Unmount the boot disk

bash# cd /
bash# umount /mnt

3.8. Prepare the root disk media

Insert a blank diskette labeled "root disk".

bash# mke2fs -m0 /dev/fd0
bash# mount /dev/fd0 /mnt

3.9. Build BASH

Get the bash-3.0 source code package from ftp://ftp.gnu.org/gnu/bash/ and untar it into the /usr/src directory.

Build BASH for an i386 CPU with the following commands:

bash# cd /usr/src/bash-3.0
bash# export CC="gcc -mcpu=i386"
bash# ./configure --enable-static-link \
  --enable-minimal-config --host=i386-pc-linux-gnu
bash# make
bash# strip bash

3.10. Copy BASH to the root disk

bash# mkdir /mnt/bin
bash# cp bash /mnt/bin/bash
bash# ln -s bash /mnt/bin/sh

3.11. Create device files that BASH needs

bash# mkdir /mnt/dev
bash# mknod /mnt/dev/console c 5 1

3.12. Unmount the root disk

bash# cd /
bash# umount /mnt

4. Implementation

4.1. System startup

Follow these steps to boot the system:

  • Restart the PC with the boot disk in the floppy drive.

  • When the grub> prompt appears, type kernel (fd0)/boot/vmlinuz init=/bin/sh root=/dev/fd0 load_ramdisk=1 prompt_ramdisk=1 and press Enter.

  • After the kernel loads, type boot and press Enter.

  • Insert the root disk when prompted.

If all goes well the screen should look something like the example shown below.

GNU GRUB version 0.95

grub> kernel (fd0)/boot/vmlinuz init=/bin/sh root=/dev/fd0 load_ramdisk=1 prompt_ramdisk=1
   [Linux-bzImage, setup=0xc00, size=0xce29b]

grub> boot

Linux version 2.4.26
..
.. [various kernel messages]
..
VFS: Insert root floppy disk to be loaded into RAM disk and press ENTER
RAMDISK: ext2 filesystem found at block 0
RAMDISK: Loading 1440 blocks [1 disk] into ram disk... done.
VFS: Mounted root (ext2 filesystem) readonly.
Freeing unused kernel memory: 178k freed
# _

4.2. Testing what works

Try out a few of BASH's built-in commands to see if things are working properly.

bash# echo "Hello World"
bash# cd /
bash# pwd
bash# echo *

4.3. Noting what does not work

Try out a few other familiar commands.

bash# ls /var
bash# mkdir /var/tmp

Notice that only commands internal to BASH actually work and that external commands like ls and mkdir do not work at all. This shortcoming is something that can be addressed in a future phase of the project. For now we should just enjoy the fact that our prototype boot / root diskset works and that it was not all that hard to build.

4.4. System shutdown

Remove the diskette from fd0 and restart the system using CTRL-ALT-DELETE.