Sunteți pe pagina 1din 11

Boot Sequence

When a computer is switched on or reset, it runs through a series of diagnostics called POST -
Power-On Self-Test. This sequence culminates in locating a bootable device, such as a floppy,
cdrom or a harddisk in the order that the firmware is configured to.

The Bootsector
The first 512 bytes of a disk are known as the bootsector or Master Boot Record. The boot sector
is an area of the disk reserved for booting purposes. If the bootsector of a disk contains a valid boot
sector (the last word of the sector must contain the signature 0xAA55), then the disk is treated by
the BIOS as bootable.

To boot an operating system, the BIOS runtime searches for devices that are both active and
bootable in the order of preference defined by the complementary metal oxide semiconductor
(CMOS) settings. A boot device can be a floppy disk, a CD-ROM, a partition on a hard disk, a
device on the network, or even a USB flash memory stick.

Commonly, Linux is booted from a hard disk, where the Master Boot Record (MBR) contains the
primary boot loader. The MBR is a 512-byte sector, located in the first sector on the disk (sector 1
of cylinder 0, head 0). After the MBR is loaded into RAM, the BIOS yields control to it.

Stage 1 boot loader

The primary boot loader that resides in the MBR is a 512-byte image containing both program code
and a small partition table (see Figure 2). The first 446 bytes are the primary boot loader, which
contains both executable code and error message text. The next sixty-four bytes are the partition
table, which contains a record for each of four partitions (sixteen bytes each). The MBR ends with
two bytes that are defined as the magic number (0xAA55). The magic number serves as a validation
check of the MBR.
The job of the primary boot loader is to find and load the secondary boot loader (stage 2). It does
this by looking through the partition table for an active partition. When it finds an active partition, it
scans the remaining partitions in the table to ensure that they're all inactive. When this is verified,
the active partition's boot record is read from the device into RAM and executed.

Stage 2 boot loader

The secondary, or second-stage, boot loader could be more aptly called the kernel loader. The task
at this stage is to load the Linux kernel and optional initial RAM disk.
The first- and second-stage boot loaders combined are called Linux Loader (LILO) or GRand
Unified Bootloader (GRUB) in the x86 PC environment. Because LILO has some disadvantages
that were corrected in GRUB, let's look into GRUB.

The great thing about GRUB is that it includes knowledge of Linux file systems. Instead of using
raw sectors on the disk, as LILO does, GRUB can load a Linux kernel from an ext2 or ext3 file
system. It does this by making the two-stage boot loader into a three-stage boot loader. Stage 1
(MBR) boots a stage 1.5 boot loader that understands the particular file system containing the Linux
kernel image. Examples include reiserfs_stage1_5 (to load from a Reiser journaling file
system) or e2fs_stage1_5 (to load from an ext2 or ext3 file system). When the stage 1.5 boot
loader is loaded and running, the stage 2 boot loader can be loaded.
With stage 2 loaded, GRUB can, upon request, display a list of available kernels (defined in
/etc/grub.conf, with soft links from /etc/grub/menu.lst and /etc/grub.conf).
You can select a kernel and even amend it with additional kernel parameters. Optionally, you can
use a command-line shell for greater manual control over the boot process.
With the second-stage boot loader in memory, the file system is consulted, and the default kernel
image and initrd image are loaded into memory. With the images ready, the stage 2 boot loader
invokes the kernel image.
With the kernel image in memory and control given from the stage 2 boot loader, the kernel stage
begins. The kernel image isn't so much an executable kernel, but a compressed kernel image.
Typically this is a zImage (compressed image, less than 512KB) or a bzImage (big compressed
image, greater than 512KB), that has been previously compressed with zlib. At the head of this
kernel image is a routine that does some minimal amount of hardware setup and then decompresses
the kernel contained within the kernel image and places it into high memory. If an initial RAM disk
image is present, this routine moves it into memory and notes it for later use. The routine then calls
the kernel and the kernel boot begins.

When the bzImage (for an i386 image) is invoked, you begin at ./arch/i386/boot/head.S
in the start assembly routine (see Figure 3 for the major flow). This routine does some basic
hardware setup and invokes the startup_32 routine
in ./arch/i386/boot/compressed/head.S. This routine sets up a basic environment
(stack, etc.) and clears the Block Started by Symbol (BSS). The kernel is then decompressed
through a call to a C function called decompress_kernel (located
in ./arch/i386/boot/compressed/misc.c). When the kernel is decompressed into
memory, it is called. This is yet another startup_32 function, but this function is
in ./arch/i386/kernel/head.S.

In the new startup_32 function (also called the swapper or process 0), the page tables are
initialized and memory paging is enabled. The type of CPU is detected along with any optional
floating-point unit (FPU) and stored away for later use. The start_kernel function is then
invoked (init/main.c), which takes you to the non-architecture specific Linux kernel. This is,
in essence, the main function for the Linux kernel.
Memory Regions
Steps for the lab
Download and install NASM from
sudo apt-get install nasm

Create a first Bootloader that does nothing

create a file firstBootLoader.asm

[BITS 16] ;tell the assembler that its a 16 bit code

[ORG 0x7C00] ;Origin, tell the assembler that where the code will
;be in memory after it is been loaded

JMP $ ;infinite loop

TIMES 510 - ($ - $$) db 0 ;fill the rest of sector with 0

DW 0xAA55 ; add boot signature at the end of bootloader


[BITS 16]: Our code starts with [BITS 16] which is an assembler directive. This will tell
assembler that our code is a 16 bit code.

[ORG 0x7C00]: Then we have used [ORG 0x7C00] which tell assembler to assemble the
instructions from Origin 0x7C00. BIOS loads bootloader at physical address 0x7C00 hence we
have assemble our bootloader starting from that location.

JMP $: JMP at location $ means jumping to the same location. Thus this nothing but an infinite
loop. We just want to hang our code here.

TIMES 510 - ($ - $$) db 0: As bootloader is always of length 512 bytes, our code does
not fit in this size as its small. We need to use rest of memory and hence we clear it out using
TIMES directive. $ stands for start of instruction and $$ stands for start of program. Thus ($ - $$)
means length of our code.

DW 0xAA55: This is boot signature. This tells the BIOS that this is a valid bootloader. If bios does
not get 0x55 and 0xAA at the end of the bootloader than it will treat bootloader as invalid. Thus we
provide this two bytes at the end of our bootloader.
Compile the program
nasm firstBootLoader.asm -f bin -o boot.bin

Create a floppy image using

dd if=boot.bin bs=512 of=floppy1.img

Attach the floppy image to the Virtual machine and boot. You should see a blank screen

Create 2nd Bootloader that prints ‘A’ on the screen

we will use BIOS video interrupt int 0x10.
INT 0x10 is a BIOS video interrupt. All the video related calls are made through
this interrupt.
To use this interrupt we need to set the values of some register.
AL = ASCII value of character to display
AH = 0x0E ;Teletype mode (This will tell bios that we want to print one
character on screen)
BL = Text Attribute (This will be the fore ground and background color
of character to be displayed. 0x07 in our case.)
BH = Page Number (0x00 for most of the cases)

Once all the registers all filled with appropriate value, we can call interrupt.

Code saved in secBootLoader.asm

[BITS 16] ;Tells the assembler that its a 16 bit code
[ORG 0x7C00] ;Origin, tell the assembler that where the code will
;be in memory after it is been loaded

MOV AL, 65
CALL PrintCharacter
JMP $ ;Infinite loop, hang it here.

PrintCharacter: ;Procedure to print character on screen

;Assume that ASCII value is in register AL
MOV AH, 0x0E ;Tell BIOS that we need to print one charater on screen.
MOV BH, 0x00 ;Page no.
MOV BL, 0x07 ;Text attribute 0x07 is lightgrey font on black background

INT 0x10 ;Call video interrupt

RET ;Return to calling procedure

TIMES 510 - ($ - $$) db 0 ;Fill the rest of sector with 0

DW 0xAA55 ;Add boot signature at the end of bootloader

Repeat previous two compile and image creation steps

nasm SecBootLoader.asm -f bin -o boot2.bin

dd if=boot2.bin bs=512 of=floppy2.img

We should see a ‘A’ on the screen

Third try: Hello World Bootloader

Its time to create our final Hello World bootloader. We have enough expirence now and can code it
without wasting a second. So once again start your favourite text editor and start writing following
jmp main
mov ax, 07C0h
add ax, 288
mov ss, ax
mov sp, 4096
mov ax, 07C0h
mov ds, ax
call PrintHelloWorld
jmp .InfiniteLoop
jmp .InfiniteLoop

HelloWorld db "Hello World. This is from the bootloader", 0x0d, 0x0a, 0x00
mov si, HelloWorld
call PrintStr
push ax
mov ah, 0Eh
cmp al, 0x00
je .done
int 10h
jmp .loop
pop ax
times 510-($-$$) db 0
dw 0xAA55

Compile and run like last time

nasm thirdBootLoader.asm -f bin -o boot3.bin

dd if=boot3.bin bs=512 of=floppy3.img

load in Virtual machine and see Hello World printed

Boot Loader 4 – Load a kernel

Create a directory structure

mkdir -p isofiles/boot/grub

You now created an isofiles directory in your home folder and a

boot/grub sub-folder inside that. We need the stage2 file. It is
installed from the grub package. Just copy it:
cp /usr/lib/grub/i386-pc/stage2_eltorito ~/isofiles/boot/grub/

get stage2_eltoritor from and put it in the grub dir

Install a kernel
Get a kernel of your own choosing and copy it to wherever you like inside the isofiles folder.
Preferably it should be placed in the boot sub-folder. Now create a menu.lst file. This file
controls what menu entries GRUB should provide and how the kernels are booted. It must be placed
in the GRUB folder boot/grub and contain something like this:

Copy a compiled kernel from your machine – it will not work but explains how
default 0
#timeout 30

#title Boot from hard disk

#chainloader (hd0)+1

title My kernel
kernel /boot/vmlinuz-4.8.15-300.fc25.x86_64 # Edit it to the filename of your

Create the .iso image

In the following I use the command genisoimage, but you can change it to mkisofs if that is
what its called on your system. Open a command prompt/terminal and go to where the isofiles
folder is located. It is your home directory on Ubuntu. Issue the command:
genisoimage -R -b boot/grub/stage2_eltorito -no-emul-boot -boot-load-size 4 -boot-info-table -o
bootable.iso isofiles

Load the ISO into virtual machine and boot.

Design Review
For this project, at your design review, we want you to write print_char and
print_string functions in assembly. The first outputs a single character to the
screen, and the second outputs a full string to the screen. Both should start
printing at the cursor and advance the cursor as a result. You may implement
these using any BIOS interrupts. Note that these are functions: they will be
called with call and return with ret, and they should save and restore any
modified registers. You may choose your own calling convention. The goal for
this part (what we're grading) is your understanding of the stack and using
interrupt calls. This is more important than getting the two functions to work
You should be able to describe:
• How to move the kernel from disk to memory
• Where to find it on disk
• Where to copy it in memory
• How to do the copying in assembly
• In what case must your bootloader be relocated?
• How to create the disk image
• Given an executable (ELF), show how to use the header info to find where the first
code segment begins
• Show how to determine where in the image file this segment must be placed.
• Where to write padding