Multiboot2 Bare Bones: Difference between revisions

Jump to navigation Jump to search
[unchecked revision][unchecked revision]
Content added Content deleted
m (Remove EPLOS reference.)
m (Replace deprecated <source> tag with <syntaxhighlight>)
 
Line 169: Line 169:


You can then assemble boot.s using:
You can then assemble boot.s using:
<source lang="bash">i686-elf-as boot.s -o boot.o</source>
<syntaxhighlight lang="bash">i686-elf-as boot.s -o boot.o</syntaxhighlight>


== Implementing the Kernel ==
== Implementing the Kernel ==
Line 185: Line 185:
<b>IMPORTANT NOTE</b>: the VGA text mode (as well as the BIOS) is deprecated on newer machines, and UEFI only supports pixel buffers. For forward compatibility you might want to start with that. Ask [[GRUB]] to set up a framebuffer using appropriate Multiboot flags or call [[Vesa|VESA VBE]] yourself. Unlike VGA text mode, a framebuffer has pixels, so you have to draw each glyph yourself. This means you'll need a different <code>terminal_putchar</code>, and you'll need a font (bitmap images for each character). All Linux distro ships [[PC Screen Font]]s that you can use, and the wiki article has a simple putchar() example. Otherwise everything else described here still stands (you have to keep track of the cursor position, implement line breaks and scrolling etc.)
<b>IMPORTANT NOTE</b>: the VGA text mode (as well as the BIOS) is deprecated on newer machines, and UEFI only supports pixel buffers. For forward compatibility you might want to start with that. Ask [[GRUB]] to set up a framebuffer using appropriate Multiboot flags or call [[Vesa|VESA VBE]] yourself. Unlike VGA text mode, a framebuffer has pixels, so you have to draw each glyph yourself. This means you'll need a different <code>terminal_putchar</code>, and you'll need a font (bitmap images for each character). All Linux distro ships [[PC Screen Font]]s that you can use, and the wiki article has a simple putchar() example. Otherwise everything else described here still stands (you have to keep track of the cursor position, implement line breaks and scrolling etc.)


<source lang="c">
<syntaxhighlight lang="c">
#include <stdbool.h>
#include <stdbool.h>
#include <stddef.h>
#include <stddef.h>
Line 300: Line 300:
terminal_writestring("Hello, kernel World!\n");
terminal_writestring("Hello, kernel World!\n");
}
}
</syntaxhighlight>
</source>


Notice how in the code you wished to use the common C function <tt>strlen</tt>, but this function is part of the C standard library that you don't have available. Instead, you relied on the freestanding header <stddef.h> to provide <tt>size_t</tt> and you simply declared your own implementation of <tt>strlen</tt>. You will have to do this for every function you wish to use (as the freestanding headers only provide macros and data types).
Notice how in the code you wished to use the common C function <tt>strlen</tt>, but this function is part of the C standard library that you don't have available. Instead, you relied on the freestanding header <stddef.h> to provide <tt>size_t</tt> and you simply declared your own implementation of <tt>strlen</tt>. You will have to do this for every function you wish to use (as the freestanding headers only provide macros and data types).


Compile using:
Compile using:
<source lang="bash">i686-elf-gcc -c kernel.c -o kernel.o -std=gnu99 -ffreestanding -O2 -Wall -Wextra</source>
<syntaxhighlight lang="bash">i686-elf-gcc -c kernel.c -o kernel.o -std=gnu99 -ffreestanding -O2 -Wall -Wextra</syntaxhighlight>


Note that the above code uses a few extensions and hence you build as the GNU version of C99.
Note that the above code uses a few extensions and hence you build as the GNU version of C99.
Line 315: Line 315:
You can compile the file kernel.c++ using:
You can compile the file kernel.c++ using:


<source lang="bash">i686-elf-g++ -c kernel.c++ -o kernel.o -ffreestanding -O2 -Wall -Wextra -fno-exceptions -fno-rtti</source>
<syntaxhighlight lang="bash">i686-elf-g++ -c kernel.c++ -o kernel.o -ffreestanding -O2 -Wall -Wextra -fno-exceptions -fno-rtti</syntaxhighlight>


Note that you must have also built a cross C++ compiler for this work.
Note that you must have also built a cross C++ compiler for this work.
Line 323: Line 323:
You can now assemble boot.s and compile kernel.c. This produces two object files that each contain part of the kernel. To create the full and final kernel you will have to link these object files into the final kernel program, usable by the bootloader. When developing user-space programs, your toolchain ships with default scripts for linking such programs. However, these are unsuitable for kernel development and you need to provide your own customized linker script. Save the following in linker.ld:
You can now assemble boot.s and compile kernel.c. This produces two object files that each contain part of the kernel. To create the full and final kernel you will have to link these object files into the final kernel program, usable by the bootloader. When developing user-space programs, your toolchain ships with default scripts for linking such programs. However, these are unsuitable for kernel development and you need to provide your own customized linker script. Save the following in linker.ld:


<source lang="c">
<syntaxhighlight lang="c">
/* The bootloader will look at this image and start execution at the symbol
/* The bootloader will look at this image and start execution at the symbol
designated as the entry point. */
designated as the entry point. */
Line 367: Line 367:
a segment with the same name. Simply add stuff here as needed. */
a segment with the same name. Simply add stuff here as needed. */
}
}
</syntaxhighlight>
</source>


With these components you can now actually build the final kernel. We use the compiler as the linker as it allows it greater control over the link process. Note that if your kernel is written in C++, you should use the C++ compiler instead.
With these components you can now actually build the final kernel. We use the compiler as the linker as it allows it greater control over the link process. Note that if your kernel is written in C++, you should use the C++ compiler instead.
Line 373: Line 373:
You can then link your kernel using:
You can then link your kernel using:


<source lang="bash">i686-elf-gcc -T linker.ld -o myos.bin -ffreestanding -O2 -nostdlib boot.o kernel.o -lgcc</source>
<syntaxhighlight lang="bash">i686-elf-gcc -T linker.ld -o myos.bin -ffreestanding -O2 -nostdlib boot.o kernel.o -lgcc</syntaxhighlight>


Note: Some tutorials suggest linking with i686-elf-ld rather than the compiler, however this prevents the compiler from performing various tasks during linking.
Note: Some tutorials suggest linking with i686-elf-ld rather than the compiler, however this prevents the compiler from performing various tasks during linking.
Line 383: Line 383:
If you have [[GRUB]] installed, you can check whether a file has a valid [[Multiboot2]] version header, which is the case for your kernel. It's important that the Multiboot2 header is within the first 8 KiB of the actual program file at 4 byte alignment. This can potentially break later if you make a mistake in the boot assembly, the linker script, or anything else that might go wrong. If the header isn't valid, GRUB will give an error that it can't find a Multiboot2 header when you try to boot it. This code fragment will help you diagnose such cases:
If you have [[GRUB]] installed, you can check whether a file has a valid [[Multiboot2]] version header, which is the case for your kernel. It's important that the Multiboot2 header is within the first 8 KiB of the actual program file at 4 byte alignment. This can potentially break later if you make a mistake in the boot assembly, the linker script, or anything else that might go wrong. If the header isn't valid, GRUB will give an error that it can't find a Multiboot2 header when you try to boot it. This code fragment will help you diagnose such cases:


<source lang="bash">
<syntaxhighlight lang="bash">
grub-file --is-x86-multiboot2 myos.bin
grub-file --is-x86-multiboot2 myos.bin
</syntaxhighlight>
</source>


<tt>grub-file</tt> is quiet but will exit 0 (successfully) if it is a valid multiboot2 kernel and exit 1 (unsuccessfully) otherwise. You can type <tt>echo $?</tt> in your shell immediately afterwards to see the exit status. You can add this grub-file check to your build scripts as a sanity test to catch the problem at compile time. Multiboot version 1 can be checked with the <tt>--is-x86-multiboot1</tt> option instead. If you invoke the <tt>grub-file</tt> command manually in a shell, it is convenient to wrap it in a conditional to easily see the status. This command should work now:
<tt>grub-file</tt> is quiet but will exit 0 (successfully) if it is a valid multiboot2 kernel and exit 1 (unsuccessfully) otherwise. You can type <tt>echo $?</tt> in your shell immediately afterwards to see the exit status. You can add this grub-file check to your build scripts as a sanity test to catch the problem at compile time. Multiboot version 1 can be checked with the <tt>--is-x86-multiboot1</tt> option instead. If you invoke the <tt>grub-file</tt> command manually in a shell, it is convenient to wrap it in a conditional to easily see the status. This command should work now:


<source lang="bash">
<syntaxhighlight lang="bash">
if grub-file --is-x86-multiboot2 myos.bin; then
if grub-file --is-x86-multiboot2 myos.bin; then
echo multiboot2 confirmed
echo multiboot2 confirmed
Line 395: Line 395:
echo the file is not multiboot2
echo the file is not multiboot2
fi
fi
</syntaxhighlight>
</source>


== Booting the Kernel ==
== Booting the Kernel ==
Line 403: Line 403:
You can easily create a bootable CD-ROM image containing the GRUB bootloader and your kernel using the program <tt>grub-mkrescue</tt>. You may need to install the GRUB utility programs and the program <tt>xorriso</tt> (version 0.5.6 or higher). First you should create a file called grub.cfg containing the contents:
You can easily create a bootable CD-ROM image containing the GRUB bootloader and your kernel using the program <tt>grub-mkrescue</tt>. You may need to install the GRUB utility programs and the program <tt>xorriso</tt> (version 0.5.6 or higher). First you should create a file called grub.cfg containing the contents:


<source lang="c">
<syntaxhighlight lang="c">
menuentry "myos" {
menuentry "myos" {
multiboot2 /boot/myos.bin
multiboot2 /boot/myos.bin
}
}
</syntaxhighlight>
</source>


Note that the braces must be placed as shown here. You can now create a bootable image of your operating system by typing these commands:
Note that the braces must be placed as shown here. You can now create a bootable image of your operating system by typing these commands:
<source lang="bash">
<syntaxhighlight lang="bash">
mkdir -p isodir/boot/grub
mkdir -p isodir/boot/grub
cp myos.bin isodir/boot/myos.bin
cp myos.bin isodir/boot/myos.bin
cp grub.cfg isodir/boot/grub/grub.cfg
cp grub.cfg isodir/boot/grub/grub.cfg
grub-mkrescue -o myos.iso isodir
grub-mkrescue -o myos.iso isodir
</syntaxhighlight>
</source>


Congratulations! You have now created a file called myos.iso that contains your Hello World operating system. If you don't have the program <tt>grub-mkrescue</tt> installed, now is a good time to install GRUB. It should already be installed on Linux systems. Windows users will likely want to use a Cygwin variant if no native grub-mkrescue program is available.
Congratulations! You have now created a file called myos.iso that contains your Hello World operating system. If you don't have the program <tt>grub-mkrescue</tt> installed, now is a good time to install GRUB. It should already be installed on Linux systems. Windows users will likely want to use a Cygwin variant if no native grub-mkrescue program is available.
Line 428: Line 428:
Install QEMU from your repositories, and then use the following command to start your new operating system.
Install QEMU from your repositories, and then use the following command to start your new operating system.


<source lang="bash">qemu-system-i386 -cdrom myos.iso</source>
<syntaxhighlight lang="bash">qemu-system-i386 -cdrom myos.iso</syntaxhighlight>


This should start a new virtual machine containing only your ISO as a cdrom. If all goes well, you will be met with a menu provided by the bootloader. Simply select myos and if all goes well, you should see the happy words "Hello, Kernel World!" followed by some mysterious character.
This should start a new virtual machine containing only your ISO as a cdrom. If all goes well, you will be met with a menu provided by the bootloader. Simply select myos and if all goes well, you should see the happy words "Hello, Kernel World!" followed by some mysterious character.
Line 438: Line 438:
Or alternatively, you can burn it to an USB stick (erasing all data on it!). To do so, simply find out the name of the USB block device, in my case /dev/sdb but this may vary, and using the wrong block device (your harddisk, gasp!) may be disastrous. If you are using Linux and /dev/sdx is your block name, simply:
Or alternatively, you can burn it to an USB stick (erasing all data on it!). To do so, simply find out the name of the USB block device, in my case /dev/sdb but this may vary, and using the wrong block device (your harddisk, gasp!) may be disastrous. If you are using Linux and /dev/sdx is your block name, simply:


<source lang="bash">sudo dd if=myos.iso of=/dev/sdx && sync</source>
<syntaxhighlight lang="bash">sudo dd if=myos.iso of=/dev/sdx && sync</syntaxhighlight>


Your operating system will then be installed on your USB stick. If you configure your BIOS to boot from USB first, you can insert the USB stick and your computer should start your operating system.
Your operating system will then be installed on your USB stick. If you configure your BIOS to boot from USB first, you can insert the USB stick and your computer should start your operating system.