CLOSE

In the early days booting an operating system is one of the critical and complex process in computing. It involves loading the kernel into memory, initializing hardware, and transferring control to the operating system. However, the lack of standardization in the early days of computing made it challenging to develop bootloaders and operating systems that work seamlessly across different hardware platforms. There comes Multiboot, a specification designed to simplify and standardize the boot process.

What is Multiboot Specification ❔

The Multiboot specification defines an interface between the bootloader and the operating system kernel. It was created to eliminate the need for OS developers to write their own bootloaders or modify existing ones. By adhering to the Multiboot standard, developers can ensure that their OS kernel can be loaded by any compliant bootloader, thus streamlining the boot process and improving compatibility.

It is an open standard that defines a protocol for bootloader to load operating system kernels.

It was created to address the challenges of booting multiple operating system on different hardware architectures. The Multiboot specification provides a common interface between bootloaders and kernels, ensuring compatibility and simplifying the boot process.

The most widely known implementation of the Multiboot Specification is the GRUB (Grand Unified Bootloader) bootloader, which is commonly used in Linux-based systems. By adhering to the Multiboot standard, GRUB can load any Multiboot-compliant kernel, regardless of the operating system.

Key Features of Multiboot

  1. Unified Interface: Multiboot provides a standardized way for bootloaders to pass information to the kernel. This includes memory maps, boot device details, command-line arguments, and more.
  2. Compatibility: By following the Multiboot specification, an OS can be booted by any Multiboot-compliant bootloader, enhancing the OS's portability.
  3. Flexibility: The specification supports various file formats, including ELF and a.out, making it versatile for different development environments.

Why Was Multiboot Created?

Before Multiboot, bootloaders were often tightly coupled with specific operating systems. This meant that each operating system requires its own custom bootloader, leading to fragmentation and compatibility issues. For example, booting a Linux kernel required different bootloader than booting a Windows or BSD kernel.

Multiboot was created to solve this problem by providing a standardized way for bootloaders to load kernels. This standardization offers several benefits:

  1. Cross-Platform Compatibility: Multiboot-compliant bootloaders can load any Multiboot-complaint kernel, regardless of the operating system or hardware architecture.
  2. Simplified Development: Operating system developers no longer need to write custom bootloaders for their kernels.
  3. Flexibility: Users can boot multiple operating systems from a single bootloader, making it easier to dual-boot or test different OSes.

Why is Multiboot Needed?

  1. Interoperability: It ensures that various kernels can be booted using a standard bootloader without needing custom bootloaders for each OS.
  2. Flexibility: Allows users to choose from multiple operating systems at boot time.
  3. Standardization: Provides a consistent method for passing boot information from the bootloader to the kernel.
  4. Simplification: Eases the development of operating systems by allowing developers to focus on the kernel without worrying about the specifics of different bootloaders.

Key Components of Multiboot

The Multiboot Specification defines several key components that work together to enable a standardized boot process:

  1. Multiboot Header:
    • The Multiboot Header is a data structure embedded within the kernel image. It contains a magic number (0x1BADB002), flags, and a checksum. The bootloader uses this header to identify and load the kernel.
  2. Bootloader:
    • The bootloader (e.g., GRUB) is responsible for loading the kernel into memory and transferring control to it. It reads the Multiboot Header to determine how to load the kernel and what resources to provide.
  3. Kernel:
    • The kernel is the core of the operating system. A Multiboot-compliant kernel includes a Multiboot Header and adheres to the Multiboot Specification.
  4. Information Structure:
    • After loading the kernel, the bootloader passes a structure containing information about the system (e.g., memory layout, boot device) to the kernel. This information helps the kernel initialize and run correctly.

How Multiboot Works ?

The Multiboot boot process involves several steps:

  1. Bootloader Initialization:
    • When the computer starts, the BIOS or UEFI firmware loads the bootloader (e.g., GRUB) into memory and executes it.
  2. Kernel Detection:
    • The bootloader scans the kernel image for the Multiboot Header. It looks for the magic number 0x1BADB002 to identify a Multiboot-complaint kernel.
  3. Kernel Loading:
    • Once the kernel is identified, the bootloader reads the Multiboot Header to determine how to to load the kernel. It loads the kernel into memory and prepares the system for execution.
  4. Information Passing:
    • The bootloader passes a structure containing system information (e.g., memory map, boot device) to the kernel. This information is crucial for the kernel to initialize hardware and manage resources.
  5. Kernel Execution:
    • The bootloader transfers control to the kernel's entry point. The kernel takes over and begins the operating system's initialization process.

The Multiboot Header ?

The Multiboot header is a crucial component that must be included in the OS kernel binary. This header contains information that the bootloader uses to load and start the kernel. Key fields in the Multiboot header include:

  • Magic Number: A specific value (0x1BADB002) that identifies the file as Multiboot-compliant.
    • It is specific 32-bit magic number used in the Multiboot Specification.
  • Flags: These specify the features required by the kernel, such as memory information, video mode settings, and more.
  • Checksum: A value that ensures the header's integrity by verifying that the sum of the magic number, flags, and checksum is zero.

What Data is Passed to the Kernel ?

The bootloader passes two main pieces of data to the kernel:

  1. Multiboot Magic Number:
    • A 32-bit value (0x2BADB002) that confirms the kernel was loaded by a Multiboot-complaint bootloader. This serves as a verification mechanism to ensure the bootloader followed the Multiboot protocol.
  2. Multiboot Information Structure:
    • A pointer to a data structure (often referred to as the multiboot_info structure) that contains detailed information about the system's hardware and boot environment. This structure includes:
      1. Memory layout (available RAM, reserved memory, etc.).
      2. Boot device information.
      3. Command-line arguments passed to the kernel.
      4. Loaded modules (e.g., drivers or initramfs).
      5. Memory map (physical memory regions and their types).
      6. Other optional information (e.g., BIOS drive data, APM table, etc.).

Where and How is the Data Passed to the Kernel?

The data is passed to the kernel via CPU registers and memory pointers. The exact mechanism depends on the architecture, but for x86 systems, the following conventions are used:

  1. Multiboot Magic Number:
    • The magic number is passed in the EAX register.
    • The kernel must check that this value is 0x2BADB002 to confirm it was loaded by a Multiboot-compliant bootloader.
  2. Multiboot Information Structure:
    • A pointer to the multiboot_info structure is passed in the EBX register.
    • The kernel can dereference this pointer to access the structure and retrieve system information.

Below is the textual flow chart of it:

+-------------------+
|  Bootloader Starts |
|  (e.g., GRUB)      |
+-------------------+
          |
          v
+-------------------+
|  Bootloader Loads  |
|  Kernel Image      |
+-------------------+
          |
          v
+-------------------+
|  Bootloader Checks |
|  for Multiboot     |
|  Header in Kernel  |
|  (Magic Number:    |
|   0x1BADB002)      |
+-------------------+
          |
          v
+-------------------+
|  Bootloader Loads  |
|  Kernel into Memory|
|  and Prepares      |
|  Multiboot Info    |
|  Structure         |
+-------------------+
          |
          v
+-------------------+
|  Bootloader Sets   |
|  Registers:        |
|  - EAX = 0x2BADB002|
|  - EBX = Pointer   |
|    to Multiboot    |
|    Info Structure  |
+-------------------+
          |
          v
+-------------------+
|  Bootloader Jumps  |
|  to Kernel Entry   |
|  Point             |
+-------------------+
          |
          v
+-------------------+
|  Kernel Entry Point|
|  (Assembly Code)   |
|  - Verify EAX      |
|    (Magic Number)  |
|  - Set Up Stack    |
|  - Call kernel_main|
+-------------------+
          |
          v
+-------------------+
|  kernel_main       |
|  (C Function)      |
|  - Parse Multiboot |
|    Info Structure  |
|  - Initialize      |
|    Kernel          |
+-------------------+
          |
          v
+-------------------+
|  Kernel Takes      |
|  Control of System |
+-------------------+

Step-by-Step Guide to Creating a Multiboot-Compatible Kernel

Step 1: Understand the Multiboot Specification

The Multiboot Specification outlines the requirements for a kernel to be loaded by a Multiboot-compliant bootloader. Key requirements include a Multiboot header and specific information structures passed by the bootloader to the kernel.

Step 2: Write a Multiboot Header

The kernel must contain a Multiboot header that the bootloader can recognize. This header includes:

  • Magic Number: A specific value (0x1BADB002) that the bootloader looks for to identify a Multiboot-compliant kernel.
  • Flags: Indicate features supported by the kernel, like memory information, video mode, etc.
  • Checksum: Ensures the integrity of the header by making the sum of the magic number, flags, and checksum equal to zero.

Multiboot Header: The Multiboot header contains the magic number, flags, and checksum that the bootloader uses to identify and load the kernel. This header must be within the first 8KB of the kernel binary.

The kernel must include a Multiboot header. Here's an example written in assembly:

; multiboot_header.asm

SECTION .multiboot
ALIGN 4

multiboot_header:
    dd 0x1BADB002              ; Magic number
    dd 0x00                    ; Flags
    dd -(0x1BADB002 + 0x00)    ; Checksum

; The header must be within the first 8KB of the kernel
SECTION .text
global start

start:
    ; Kernel entry point
    cli
    hlt

Step 3: Write a Simple Kernel

Kernel Code: A simple example kernel is provided that clears the screen and prints “Hello, Multiboot!” to the video memory. This kernel has an entry point (start), which is specified in the linker script.

Here's an example of a simple kernel in C that prints a message:

// kernel.c

void kmain(void) {
    const char *str = "Hello, Multiboot!";
    char *vidptr = (char*)0xb8000;  // Video memory address
    unsigned int i = 0;
    unsigned int j = 0;

    // Clear the screen
    while (j < 80 * 25 * 2) {
        vidptr[j] = ' ';
        vidptr[j+1] = 0x07; // Attribute-byte: light grey on black screen
        j += 2;
    }

    // Write the string to video memory
    j = 0;
    while (str[j] != '\0') {
        vidptr[i] = str[j];
        vidptr[i+1] = 0x07;
        ++j;
        i += 2;
    }

    // Infinite loop to keep the kernel running
    while (1) {}
}

Step 4: Link the Kernel and Multiboot Header

Linker Script: The linker script ensures that the Multiboot header and kernel code are correctly placed in memory.

Create a linker script to link the kernel and Multiboot header correctly:

/* linker.ld */

ENTRY(start)

SECTIONS {
    . = 1M;                    /* Set the starting address */

    .multiboot :
    {
        *(.multiboot)
    }

    .text :
    {
        *(.text)
    }

    .rodata :
    {
        *(.rodata)
    }

    .data :
    {
        *(.data)
    }

    .bss :
    {
        *(.bss)
    }
}

Step 5: Assemble and Compile the Kernel

Assembly and C code are compiled and linked together to create the final kernel binary.

Use NASM to assemble the Multiboot header and GCC to compile the kernel:

nasm -f elf32 multiboot_header.asm -o multiboot_header.o
gcc -m32 -c kernel.c -o kernel.o
ld -m elf_i386 -T linker.ld -o kernel.bin multiboot_header.o kernel.o

Step 6: Create a Bootable ISO with GRUB

GRUB is used as the bootloader, and a bootable ISO image is created with the kernel and GRUB configuration file.

Create a directory structure for GRUB:

mkdir -p iso/boot/grub
cp kernel.bin iso/boot/kernel.bin

Create a GRUB configuration file (grub.cfg):

cat > iso/boot/grub/grub.cfg << EOF
set timeout=5
set default=0

menuentry "My Multiboot Kernel" {
    multiboot /boot/kernel.bin
    boot
}
EOF

Generate the ISO using grub-mkrescue:

grub-mkrescue -o my_os.iso iso