DEV Community

Vivek P
Vivek P

Posted on

Simple x86 Bootloader Project

Simple x86 Bootloader Project

Table of Contents

  1. Introduction
  2. Prerequisites
  3. Bootloader Code
  4. Code Explanation
  5. Bootloader Flow
  6. Building the Bootloader
  7. Testing the Bootloader
  8. Troubleshooting
  9. Further Learning
  10. Conclusion

Introduction

This project demonstrates how to create a simple bootloader using x86 assembly language. The bootloader, when executed, prints the message "Hello World I am vivek" to the screen. This serves as an educational tool to understand the basics of how a computer boots up and how to write low-level code that interacts directly with the hardware.

Prerequisites

To work with this project, you'll need:

  • Basic understanding of assembly language concepts
  • An x86 assembler (like NASM or GNU Assembler)
  • A linker (like ld)
  • A virtualization tool (like QEMU) or a physical machine to test the bootloader
  • A text editor for writing code
  • A bash-compatible shell (for Unix-like systems) or equivalent for Windows

Bootloader Code

Save the following code in a file named sample_bootable.s:

.code16
.global init

init:
   mov $string, %si
   mov $0xe, %ah

print:
   lodsb
   cmp $0, %al
   je end
   int $0x10
   jmp print

end:
   hlt

string: .asciz "Hello World I am vivek"

.fill 510-(.-init), 1, 0
.word 0xaa55
Enter fullscreen mode Exit fullscreen mode

Code Explanation

Let's break down each part of the code:

  1. .code16: This directive tells the assembler to generate 16-bit code, which is what the BIOS expects when it first starts up.

  2. .global init: This makes the init label globally visible, which is important for the linker.

  3. init:: This label marks the entry point of our program.

  4. mov $string, %si: This loads the address of our string into the SI (Source Index) register.

  5. mov $0xe, %ah: This sets the AH register to 0xe, which is the BIOS teletype output function.

  6. print:: This label marks the start of our printing loop.

  7. lodsb: This loads a byte from the address in SI into AL and increments SI.

  8. cmp $0, %al: This compares the loaded byte with 0 (our string terminator).

  9. je end: If the byte is 0, we jump to the end label.

  10. int $0x10: This calls BIOS interrupt 0x10, which prints the character in AL.

  11. jmp print: This jumps back to the print label to process the next character.

  12. end: hlt: This halts the CPU when we're done printing.

  13. string: .asciz "Hello World I am vivek": This defines our null-terminated string.

  14. .fill 510-(.-init), 1, 0: This pads our bootloader to 510 bytes with zeros.

  15. .word 0xaa55: This is the boot signature that the BIOS looks for to determine if a sector is bootable.

Bootloader Flow

  1. The BIOS loads our bootloader into memory at address 0x7C00.
  2. Our code sets up the SI register to point to our string.
  3. We enter a loop that prints each character of the string:
    • Load a character
    • Check if it's the end of the string (null terminator)
    • If not, print it and continue
  4. After printing the entire string, we halt the CPU.

Building the Bootloader

To build the bootloader, we'll use a bash script that assembles the code, links it, and prepares it for execution. Save the following script as build_bootloader.sh:

#!/usr/bin/env bash 
as sample_bootable.s -o sample_bootable.o
ld -o boot.bin --oformat binary -e init -Ttext 0x7c00 -o boot.bin sample_bootable.o
rm sample_bootable.o
echo "execute command  :- qemu-system-x86_64 boot.bin "
Enter fullscreen mode Exit fullscreen mode

Here's what each line does:

  1. as sample_bootable.s -o sample_bootable.o: Assembles our source file into an object file.
  2. ld -o boot.bin --oformat binary -e init -Ttext 0x7c00 -o boot.bin sample_bootable.o: Links the object file into a flat binary:
    • --oformat binary: Outputs a flat binary file.
    • -e init: Sets the entry point to our init label.
    • -Ttext 0x7c00: Sets the starting address to 0x7c00, where the BIOS loads the bootloader.
  3. rm sample_bootable.o: Removes the intermediate object file.
  4. The echo command reminds you how to run the bootloader in QEMU.

Make the script executable and run it:

chmod +x build_bootloader.sh
./build_bootloader.sh
Enter fullscreen mode Exit fullscreen mode

This will create your boot.bin file, which is your bootable image.

Testing the Bootloader

You can test your bootloader using QEMU:

qemu-system-x86_64 boot.bin
Enter fullscreen mode Exit fullscreen mode

This will start QEMU, load your bootloader, and you should see your message "Hello World I am vivek" printed on the screen.

Troubleshooting

If you encounter issues:

  1. Ensure your assembler and linker are correctly installed.
  2. Check that the file paths in the build script match your directory structure.
  3. Verify that QEMU is properly installed on your system.
  4. If the message doesn't appear, double-check your assembly code for typos.

Further Learning

To expand your knowledge of bootloaders and low-level programming:

  1. Modify the message or add more functionality to the bootloader.
  2. Learn about loading larger programs from disk.
  3. Study how real bootloaders transition from 16-bit real mode to 32-bit protected mode.
  4. Explore multiboot-compliant bootloaders for loading modern operating systems.
  5. Investigate UEFI (Unified Extensible Firmware Interface) bootloaders for newer systems.

Conclusion

Congratulations! You've created a simple bootloader that prints a message to the screen. This project demonstrates the basics of how a computer starts up and how we can run our own code at the very beginning of the boot process. While this bootloader is simple, it forms the foundation for understanding more complex boot processes used in real-world systems.

Remember, working with bootloaders means you're operating at a very low level. Always test in a virtual machine to avoid potentially damaging your computer's boot sector.

Happy coding and exploring the fascinating world of low-level programming!

github repo: https://github.com/Vivx701/simple_bootable

Top comments (0)