It is very helpful to use an emulator with disk images to test code. We use
bochs. It is also convenient
to use a bootloader. We use grub.
The osdever tutorial is useful, but
doesn't really lay out how to do this. I've found a single harddisk image with
both grub and my kernel image a useful route. I depended on a few sources, but
this one was particularly helpful.
To get this started, I downloaded, compiled, and installed bochs. I leave this
as an exercise for the reader. Next, I used the bochs image builder,
bximage, to build a harddisk image:
wells@pulate the elusive .bochsrc. First, you'll have to find a sample. Because I built bochs, the sample was in /usr/local/share/doc/bochs/bochsrc-sample.txt:
wells@local:~$ cp /usr/local/share/doc/bochs/bochsrc-sample.txt ~/.bochsrc sanctus:~kernel/images$ bximage
========================================================================
bximage
Disk Image Creation Tool for Bochs
$Id: bximage.c,v 1.32 2006/06/16 07:29:33 vruppert Exp $
========================================================================
Do you want to create a floppy disk image or a hard disk image?
Please type hd or fd. [hd] hd
What kind of image should I create?
Please type flat, sparse or growing. [flat] flat
Enter the hard disk size in megabytes, between 1 and 129023
[10] 10
I will create a 'flat' hard disk image with
cyl=20
heads=16
sectors per track=63
total sectors=20160
total size=9.84 megabytes
What should I name the image?
[c.img] hd.img
Writing: [] Done.
I wrote 10321920 bytes to my.img.
The following line should appear in your bochsrc:
ata0-master: type=disk, path="hd.img", mode=flat, cylinders=20, heads=16, spt=63
If you choose a different size, it is important to note the number of cylinders
reported by bximage. Next, you need to "partition"
the disk image. Happily, with a little help, fdisk
will work on files:
wells@local:~/kernel/images$ fdisk -S 63 -H 16 -C 20 hd.img
Command (m for help): n
Command action
e extended
p primary partition (1-4)
p
Partition number (1-4): 1
First cylinder (1-20, default 1): 1
Last cylinder or +size or +sizeM or +sizeK (1-20, default 20): 20
Command (m for help): a
Partition number (1-4): 1
Command (m for help): w
The partition table has been altered!
Calling ioctl() to re-read partition table.
WARNING: Re-reading the partition table failed with error 25: Inappropriate ioctl for device.
The kernel still uses the old table.
The new table will be used at the next reboot.
Syncing disks.
wells@local:~/kernel/images$
Notice that we pass the disk geometry directly to fdisk on the
command-line. Nobody seems to believe that setting the disk as bootable is necessary, but
everybody seems to do it, so I do as well. We next use the Linux loopback facility to mount
the image as a disk and create a filesystem. To do this, you must have kernel support for
it, but many distributions seem to include it in their standard kernels by default. Also,
I use losetup to attach the file to the loopback device. The
'32256' is after the first track (512 bytes/sector * 63 sectors/track). According
to some
sources, this is necessary because fdisk want to leave
the entire first track for the master boot record. Also, apparently, some bootloaders
(grub included) use the first track for multi-stage loaders.
Anyhow, using losetup:
wells@local:~/kernel/images$ sudo /sbin/losetup -o 32256 /dev/loop0 my.img
The -o flag attaches at an offset in the file. Now we can treat the image like
a device. So, we build an ext2 filesystem on it:
wells@local:~/kernel/images$ sudo /sbin/mke2fs /dev/loop0
mke2fs 1.40-WIP (14-Nov-2006)
Filesystem label=
OS type: Linux
Block size=1024 (log=0)
Fragment size=1024 (log=0)
2512 inodes, 10048 blocks
502 blocks (5.00%) reserved for the super user
First data block=1
Maximum filesystem blocks=10485760
2 block groups
8192 blocks per group, 8192 fragments per group
1256 inodes per group
Superblock backups stored on blocks:
8193
Writing inode tables: done
Writing superblocks and filesystem accounting information: done
This filesystem will be automatically checked every 33 mounts or
180 days, whichever comes first. Use tune2fs -c or -i to override.
Notice that we are using root privilege (via sudo -- but you can
do whatever floats your boat) for these operations. This seems necessary. We are next going
to mount the device, copy over some necessary files for grub, then use the
grub shell to configure the disk image for grub booting.
wells@local:~/kernel/images$ sudo mount /dev/loop0 mnt
wells@local:~/kernel/images$ sudo mkdir -p mnt/boot/grub
wells@local:~/kernel/images$ sudo cp /boot/grub/stage1 /boot/grub/stage2 /boot/grub/e2fs_stage1_5 mnt/boot/grub/
wells@local:~/kernel/images$ sudo grub --no-floppy
grub> device (hd0) hd.img
grub> geometry (hd0) 20 16 63
drive 0x80: C/H/S = 20/16/63, The number of sectors = 20160, my.img
Partition num: 0, Filesystem type is ext2fs, partition type 0x83
grub> root (hd0,0)
grub> setup (hd0)
Checking if "/boot/grub/stage1" exists... yes
Checking if "/boot/grub/stage2" exists... yes
Checking if "/boot/grub/e2fs_stage1_5" exists... yes
Running "embed /boot/grub/e2fs_stage1_5 (hd0)"... 15 sectors are embedded.
succeeded
Running "install /boot/grub/stage1 (hd0) (hd0)1+15 p (hd0,0)/boot/grub/stage2 /boot/grub/menu.lst"... succeeded
Done.
grub> quit
The --no-floppy parameter tells the grub shell not to probe the floppy disk.
The (hd0) notation is grub nomenclature for disk identification. I doubt the
root (hd0,0) is necessary in this context, but I haven't tried it without. Notice,
too, that I use a relative mount directory, mnt/, but you can of course mount
wherever your fancy is most tickled.
Now that we've added grub, we need only add the kernel image and unmount:
wells@local:~/kernel/images$ sudo cp ../kernel.bin mnt/
wells@local:~/kernel/images$ sudo umount mnt
wells@local:~/kernel/images$ sudo losetup -d /dev/loop0
I wrote the image to the root of the disk image filesystem. The last step before starting
bochs is to edit the .bochsrc and add the image. If you don't
have a ~/.bochsrc, you'll probably want to copy one from your
build source for bochs:
wells@local:~$ cp /usr/local/share/doc/bochs/bochsrc-sample.txt ~/.bochsrc
I didn't modify anything from the default, except that I added the line from our bochs
image build in the section for ata:
ata0-master: type=disk, path="/path/to/your/hd.img", mode=flat, cylinders=20, heads=16, spt=63
Substitute a sensible path for /path/to/your/. You may also want
to change the boot order, removing the floppy. Now you're ready to start bochs and execute your
image:
wells@local:~/kernel/images$ bochs -q
========================================================================
Bochs x86 Emulator 2.3
Build from CVS snapshot on August 27, 2006
========================================================================
00000000000i[ ] LTDL_LIBRARY_PATH not set. using compile time default '/usr/local/lib/bochs/plugins'
00000000000i[ ] BXSHARE not set. using compile time default '/usr/local/share/bochs'
00000000000i[ ] reading configuration from /home/wells/.bochsrc
00000000000i[ ] lt_dlhandle is 0x81b4760
00000000000i[PLGIN] loaded plugin libbx_sdl.la
00000000000i[ ] installing sdl module as the Bochs GUI
00000000000i[ ] using log file bochsout.txt
[ ... in another window ... ]
grub> kernel (hd0,0)/kernel.bin
, <0x100000:0x3000:0x3860>, shtab=0x107168
grub> boot
Something went wrong with the disk image, or your kernel image is wrong, if bochs
can't find or won't load the kernel with the kernel directive. Otherwise,
you should be good to go!
After a while, you'll tire of typing the same grub directive every time you test, so
you'll want to create a grub menu.lst.
Here is a minimalist menu, which you may place as /boot/grub/menu.lst
on your image:
default 0
timeout 0
hiddenmenu
groot=(hd0,0)
title K-OS
root (hd0,0)
kernel (hd0,0)/kernel.bin