Sunday 4 September 2011

Writing your own Boot Loader

This blog post is a following of the excellent tutorial at http://mikeos.berlios.de/write-your-own-os.html by Mike Saunders.  My thanks to him for his permission to post this information and quote his original work.



Something I've been mooching over recently is writing my own assembly code, as part of my degree I did a tiny bit of 68000 assembly and before that I'd included assembly based interrupt driven Mouse controls into my Turbo C and Turbo Pascal programs but I had never actually sat down and understood Assembly on the x86 class processor.

So I have set about learning a little bit of assembly, using a text editor and nasm.  After getting the basics I asked the question 'What will I make?'.  Assembly is a really alien world, I'd seen listings of coding in it many times, all full of strange seemingly massively repetive commands like mov, add, dx, si and other scary looking nonsense; even as an experienced Software Engineer I had a certain amount of trepidation going through the topic at hand.

But, I soon realised I knew very little about how the BIOS in the machine picks itself up and starts the operating system.  Sure I knew how to install GRUB, how to format a drive to make it bootable and add a new master boot record, but I'd never actually looked at how the software picks itself up, how it "bootstraps"... A PC is often said to try to pick itself up by its own boot straps when you turn it on.

The first bit of software run sits in some hardware chips (ROMS), this primevil code's job is to call launch an application loaded from the first sector of a disk (the first sector is the master boot record - or MBR).  This application is the boot-strapper or boot-loader application, and you can write your own...

So I hit the inter-webs for some help and came across the MikeOS project, specifically the excellent introductory boot loader tutorial, and this is where I'm going to pick up the rest of my blog post, it struck me that as brilliant as that tutorial is it doesn't really hold ones hand through the process step by step.  So I'm going to try and help you write your own MikeOS inspired boot-loader with Ubuntu (or Kubuntu or Edbuntu or Debian) you can do this on your Windows machine too.

So, lets get started, first of all you'll need to download a whole bunch of software from the internet, you'll need the following:

Development System

Ubuntu 32bit Desktop Edition - I used v10.04 LTS - so anything newer will do: 


If you want to run this tutorial as I have, get yourself a spare PC from somewhere, burn the Ubuntu ISO to a CD and install your system.  If however you've not got a spare machine at hand, then you can read on anyway...

Next, you'll need VMWare Player.  Many tutorials on writing your own boot-loader use qemu, but I'm going with VMware player because you can get it for windows and linux... so perversely if you want to follow this tutorial on a windows machine you can run VMWare player inside your Windows installation, and let it run the Ubuntu 32bit system you downloaded in the first step!... neat.

You can get WMware Player from http://www.vmware.com/products/player/overview.html  (You will need to sign up to download the Player, but it is free).

So, go now and set yourself up, if you're really really stuck setting yourself up then drop me a line, we'll have a skype about what's going on.  But I'm going to assume you're tooled up, you've install your machine (or host a new virtual machine with VMware Player under windows) with Ubuntu and you're sat looking at its desktop.

Configure Ubuntu Tools (good for Debian, Kubuntu, Lubuntu etc).

Once your Ubuntu Install is up and running set it up to look however you wish, and open a terminal window (with either the CTRL+ALT+T key stroke, or by pressing ALT+F2 and typing xterm).  Inside your console window you will now need to install some software, namely the essential items to build software with and the assembler we'll be using; which is called nasm.

So, inside your Ubuntu terminal you will need to run the command:

sudo apt-get install build-essential nasm

This will install both the build essentials and the nasm assembler in one fell swoop.

We could now create some software but we'd have nowhere safe to run it...

VMware Player

This is where VMware player comes it, it'll let us create a virtual driev image (a floppy image) into which we'll put our boot laoder, without risking our real hard drives or wssting burning CD's all the time.

You will now need to install VMWare Player into your Ubuntu machine (if you are already hosting Ubuntu from Windows don't worry about this step, use the player from windows and copy the floppy image; mentioned in a moment; to windows to run the software we create).

To install the player simply open a terminal (or use the one you already have open) navigate to wherever you have downloaded the player bundle to and use the command:

sudo sh VMware-Player-3.1.4-855356-i386.bundle

(The last item there is the file name I've downloaded, by the time you come to read this tutorial you may have downloaded a different build of the product, so use whatever file name you have exactly in place of the one above: sudo sh <file name>).

Once this starts up to install you'll be back into the land of a GUI, so you can praise whatever god you believe in and use the mouse to set up the player software.

Once the player has installed on Ubuntu you'll be able to launch it from the terminal with the command "vmplayer" or you can launch it with a mouse click from the menu via "Applications->System Tools->VMware Player".

Go a head and start the player up, we need to use it before we can get coding.

Floppy Image

The first task we have is to find somewhere to put our boot-loader when we've built it.  We don't want to use our hard drive because we still wan to boot our machine as it is... and we don't want to keep burning and wasting CD-R's... we don't want to use a USB stick to boot because they work slightly differently to normal drives... so the option left to us is a floppy.

Now, not many of us have a real floppy drive any more, infact I'm sure some of you reading this will have never held a floppy disk in your life, but don't worry, because we're going all virtual.  We're going to get VMware Player to create us a blank floppy image.

With VMware Player open, click to create a new virtual machine, select to install the operating system later, and opt for a type of "Other" and select "MS-DOS" from the drop down... we're not going to actually let this virtual machine come into being, just advance through the screens until you get to the hardware selection screen, when you see the options to allow you to select "Customize Hardware" click that button and you'll be presented with the list of devices.



Inside the customization page you should see in the list of devices "Floppy", if you left click this item you will see several options, one of which is "Use a floppy image:".  Check that option and you'll now be able to get at a button called "Create"... you guessed it, hit create and save yourself a floppy image (FLP) file somewhere (confusingly the option is "open" on the dialog, so 'open' the image to save it).



Once you have the image just cancel the creation of the virtual machine, we don't want it at this time.

So, I now have a "blankfloppy.flp" file, which I've saved out... that image can be very useful so take a copy of it somewhere as a back up...

Code

Now, I'm going to give you the code of a whole simple, well commented, boot loader (derived from the boot-loader in MikeOS).  All this assembly does is specify the stack, define a constant string and output that string to the screen.  It then sits in an infinite loop.

; This is the assembly code for our Bootloader

BITS 16 ; Instruct the system this is 16 bit code

jmp start ; Jump to the start routine, short being never to return
nop ; No operations from here

; This is the entry point put nothing else above this, not even
; include statements
start:
mov ax, 07C0h ; Setup 4k stack space after this bootloader
add ax, 288 ; (4096+515) / 16 bytes per paragraph
cli ; Disable Interrupts
mov ss, ax
mov sp, 4096
sti ; Enable Interrupts

mov ax, 07C0h ; Set data segment to the load point of
mov ds, ax ; our application

call PrintWelcome ; Call the print welcome message routine
jmp .loopHere ; Jump to the loop here label

   .loopHere:
jmp .loopHere ; Jump to the loop here label
; this is now an infinite loop
; so all our system did was boot
; welcome us and sit spinning
; on this label


;------------------------------
; Constants
;------------------------------
c_WelcomeMessage db "HELLO AND Welcome to our OS", 0x0d, 0x0a, 0x00
;------------------------------


;----------------------------------------
; Routine to print the welcome message
; this is our only procedure in the
; application, so enjoy!
;----------------------------------------
PrintWelcome:
mov si, c_WelcomeMessage ; Move the string into si
call PrintString ; Call our routine to output til 0x00
ret ; Return to the call point
;----------------------------------------


;----------------------------------------
; Print string Routine
;    PrintString (SI)
;----------------------------------------
PrintString: ; Routine to output string in SI to the Screen
push ax ; Push the AX register to the stack
mov ah, 0Eh ; int 10h 'print char' function code

.repeat:
lodsb ; Get character from string
cmp al, 0x00 ; Compare the character with zero (0)
je .done ; if the character is zero then end of string
int 10h ; Otherwise call BIOS interupt 10 to print
jmp .repeat ; And now jump back up to the repeat label

.done:
pop ax ; Pop the copy of the AX register off the stack
ret ; Return the call stack back to the call point
;----------------------------------------



; Now we need to pad the remainder of the boot sector with 0s
times 510-($-$$) db 0 ; Times is a macro for nasm to repeat an action during
; compilation of the the application binary, in this
; case to add 510 lest the amount of data and code in
;the program to the output file

; Why 510, why not 512? A boot sector being 512 bytes?... well...
; to tell the BIOS this application, when written to the MBR, is
; a boot sector, we need to add two bytes to the end of the
; application, so we call define word with 0xaa and 0x55 now

dw 0xAA55 ; Standard PC boot Signature



You can download the complete code file from:


Read the code yourself to understand what its up to... that's the key to this tutorial, you have to apply yourself to understanding the assembly, everything else I hope you're having thought about, so you're free to look at that code in detail.

With this file in hand on disk open a terminal window once again and move to that folder, so for me its under my home directory in a folder called 'asm' in a file called '1.asm'.  To get to it and to build it with nasm I'm going to perform these commands:

CTRL+ALT+T To open the terminal window
cd /home/jonb/asm To move to my home folder's asm folder
nasm -f bin -o OS.bin 1.asm
To build as binary into a file called 'OS.bin' the 1.asm source

Everything should be fine, and you should now be able to list the directory (ls) and see 'OS.bin'.

Copying Boot Sector to Virtual Floppy

So, we now need to move that OS.bin into the blank floppy image (or a copy of the blank floppy image)... so first things first, I copy my blankfloppy.flp into the /home/jonb/asm folder.

And I perform this command:

dd status=noxfer conv=notrunc if=OS.bin of=blankfloppy.flp

This does a none-truncating move of the OS.bin into 'blankfloppy.flp' (so it's not blank anymore, its now got a boot loader installed), but the flp file stays at a size of 1.44MB and the first 512 bytes of the file is overwritten with the bin file.

Running our Code

Now we want to run our boot-loader, so lets jump back to VMware Player; if we now create a virtual machine of type "MS-DOS" with a tiny disk (0.1gb), we can select the floppy (under customize hardware), ensure it is connected on power up and browse to and select the 'blankfloppy.flp' file.

Running the virtual machine now, we should see the VMWare BIOS post, it attempt to start over a network boot, fail and then it use our floppy disk image as the boot point.

And you should see out "Welcome" message from the assembler as the only thing that comes out... that is our bootloader... we have literally control at the earliest moment possible of that virtual machine.

(You could run the floppy image in qemu too if you want - see the MikeOS tutorial - link at bottom).

(If you are hosting a Ubuntu Virtual Machine, simply copy the floppy image out of Ubuntu onto the harddrive of yoru Windows machine, launch a second copy of VMware Player and create your MS-DOS type virtual machine to boot with the floppy there, you just introduce a copy back to Windows each time you change the floppy image with the 'dd' command).

Video

They say that a picture speaks a thousand words, so for you lucky people I've create a series of videos documenting the highlights of this process.


Its best if you expand the videos, and view them in HD full screen.




Credits

The boot loader code, with the exception of a little tidying up and extra commenting is copyright Mike Saunders: http://mikeos.berlios.de/write-your-own-os.html and as per his website I publish this code here completely open source (under a BSD-like license) with the caveat that any usage of this code, either directly, or as part of your own work should always carry a reference to this blog post within.

No comments:

Post a Comment