Skip to content

Instantly share code, notes, and snippets.

@R077A6r1an
Last active August 13, 2023 00:13
Show Gist options
  • Save R077A6r1an/4895fb0114cdedf145ffe6206c42543e to your computer and use it in GitHub Desktop.
Save R077A6r1an/4895fb0114cdedf145ffe6206c42543e to your computer and use it in GitHub Desktop.
A tutorial for a minimal x86_32 operating system kernel #kernel #osdev #tutorial

A kernel tutorial

This tutorial is a introduction to OS kernel development. This exsample here is the minimal requirements you will need to implement a kernel. It is still not ready for beeing the next big operating system, but you are FREE to use this exsample as your base and extending it!

Setting up your environment

This exsample assumes, you are working on Linux. If it isn't the case, install on a old computer you may have Linux Ubuntu or Linux Mint and get familiar with the command line. Staying at Windows, you can only develop with the Windows Subsystem for Linux(preffered WSLg). Else, you may not have great confort in OS development. After installed the Windows Subsystem for Linux (preffered WSLg) and a Linux distribution over the Microsoft store, you can just run the command line commands with 'wsl' written before. Now, we can download a few base packages required:

Linux: sudo apt install gcc make nasm qemu-system-x86 Windows: wsl sudo apt install gcc make nasm qemu-system-x86

Compiling the exsample

Linux: make Windows: wsl make

Running the exsample

Linux: make boot Windows: wsl make boot

Note that under Windows, the command may fail, if you haven't configured your WSL environment to execute programms in graphic mode. By running this exsample, the computer emulator qemu will start the OS kernel as a virtual maschine.

Running on bare metal

Running a operating system on bare metal isn't magic, but it requires a few more steps. For this, you will really need to install Linux.

After compiling this exsample, put the kernel 'OS-kernel' in a directory, named 'isodir'.

Now we will install the minimal required packages:

sudo apt install grub-rescue-pc mtools xorriso

Now change to the folder 'isodir/../', so the parent folder of the isodir you created. Then you can create the iso image:

grub-mkrescue -o Image.iso isodir

If it works without errors, you will have a file named Image.iso in your current working directory. Now plug in a usb storage device, and write the iso filesystem you just created on your usb device:

(assuming, your usb stick is /dev/sdb, if you have more or less then one storage device installed on your PC, than check the searched device file with running lsblk)

sudo dd if=Image.iso of=/dev/sdb

Now you just have setted up the bootable USB stick. The next step is running it on bare metal!

Before running

Plugig in your USB device in a computer and turning it on is not all you need to do. You need say before the BIOS of your computer, the Basic Input Output System (BIOS), that you wan't to start you OS from the USB stick:

For first, choose a computer as test person. You can be sure, that you will not destroy your computer with it ( I just tested all this without problems). The technical requirements are just a x86 or x64 processor architecture, and please don't use a laptop. It is possible to start it like the same way I discribe here, but a laptop hasn't setted up by default VGA as videomode. This requires additional 200 lines more assembly code, for switching in 16 bit realmode, setting in the register ax to 0x0003, call bios interrupt 0x10 and switch back to 32 bit protected mode.

After choosen a computer, search in google the bios key. Just subit a search like "[computer brand] bios key". Normaly it may be ESC, DEL or any of the F1/F2/... keys.

Now, plug in the USB device in the computer and turn it on! Directly after turning on, spam the BIOS key you just googled, till you have entered the BIOS MENU. If you get in there the first time, it may seem strage, but get familiar with it. As OS developer or just Linux User, you may play the next time a bit in this menu.

In there, go to the Boot Menu. The BIOS menu structure may variate from vendor to vendor. Fist, disable 'Secure Boot', because this will block other OS starts, except Windows. Then go to 'Boot Order', and try to set as first device the USB device you have just plugged in. Try to experiment with the keyboard keys, till you find out, how to move the USB device up. First try out with F5 and F6.

After a while, you may have success. Then go to 'Save and Exit', and save the changes.

The Computer will reboot, and enter the GRUB bootloader, installed on the USB stick. Please do not remove the USB stick from your computer during reboot and usage!!!

Starting your OS!

If all works successfully, a shell will apear on the screen. You need to enter now the following commands to start your OS:

multiboot /OS-kernel kernel parameters boot

You are free to replace 'kernel parameters' with any other parameter you wan't to pass to the kernel, or just leave free. If it works, you get on the Screen an output like following:

OS-kernel kernel parameters

Congratulations, your first real Operating System on bare metal! Now, the kernel don't have any functions to poweroff the computer. So just press the off-button, till the Computer turns it self off, for shut down your computer.

Trubleshooting

It may be, that after submit 'boot' in the GRUB shell, a message will be printen like:

No suiteable videomode found!

Or similar. That is just like I mentionated above, that why you should not use a laptop! So try another computer, or else, you will need to extend the following exsample, like above described, to set the videomode to VGA textmode.

What's next?

Now, you are able to compile a Operating System for x86 computers in 32 bit in C and x86 nasm assembly. You can now extend this exsample and add all things, a operating system requires. The same also would work if you programm in C++, but note that you need declare the kernel_main funtion as extern "C", for be callen from the assembly code. As useful resource, you can just use the following webiste:

https://wiki.osdev.org/Main_Page

// First, a minimal stdint.h
typedef __UINT32_TYPE__ uint32_t;
typedef __UINT64_TYPE__ uint64_t;
// NOTE: as kernel, we dont have any stdlib, so if you want to use one, implement one your self, and link it static!
// the multiboot info structure ( all we get by the multiboot bootloader )
struct multiboot_info {
uint32_t mbs_flags;
uint32_t mbs_mem_lower;
uint32_t mbs_mem_upper;
uint32_t mbs_bootdevice;
uint32_t mbs_cmdline;// IMPORTAINT, our kernel parameters
uint32_t mbs_mods_count;
void* mbs_mods_addr;
uint32_t mbs_syms[4];
uint32_t mbs_mmap_length;
void* mbs_mmap_addr;
} __attribute__((packed));
struct multiboot_mmap {
uint32_t entry_size;
uint64_t base;
uint64_t length;
uint32_t type;
} __attribute__((packed));
struct multiboot_module {
uint32_t mod_start;
uint32_t mod_end;
char* cmdline;
uint32_t reserved;
} __attribute__((packed));
// A VGA text output defines
static char* const VGA = (char*)0xb8000;
#define WIDTH 80
#define HIGHT 25
// the VGA array has 25 lines, with 80 characters per line
// you write in it as following:
// VGA[index*2] = '#'; // writing the character
// VGA[index*2+1] = 0x07; // setting its color as hexadezimal, as 0x[backgroundcolor][foregroundcolor]
// // here: 0x07 is gray character, black background
void kernel_main(struct multiboot_info* info) {
char* params = info->mbs_cmdline; // getting params
// now printing to screen in VGA
int i = 0; // string index counter
int j = 0; // VGA index counter
while(params[i] != '\0') {
VGA[j] = params[i];
VGA[j+1] = 0x07;
i++;
j += 2;
}
// TODO: Your turn! Implement Your kernel, implement drivers, scheduler, syscalls, memory manager, etc.
}
ENTRY (_start)
SECTIONS
{
. = 0x100000;
kernel_start = .;
.text : {
*(multiboot)
*(.text)
}
.data ALIGN(4096) : {
*(.data)
}
.bss ALIGN(4096) : {
*(.bss)
}
. = ALIGN(4096);
kernel_end = .;
}
Compiler := gcc -c
Linker := gcc -o
OUT := OS-kernel
CC_FLAGS := -m32 -nostdinc -nostdlib -ffreestanding -nodefaultlibs *.c
LD_FLAGS := $(OUT) -m32 -T linker.ld -nostdlib -nodefaultlibs *.o
CC := $(Compiler) $(CC_FLAGS)
LD := $(Linker) $(LD_FLAGS)
all:
$(CC)
nasm -felf start.asm
$(LD)
boot:
qemu-system-x86_64 -kernel OS-kernel -append "Here you have the kernel params"
bits 32 ; we are here in 32 bit protected mode
global _start ; making the _start function global for ld
extern kernel_main ; prototype for the function void kernel_main(struct multiboot_info mb)
section .text
align 4 ; first, a few constants for the multiboot header
dd 0x1BADB002 ; multiboot magic
dd 0x00 ; flags
dd - (0x1BADB002 + 0x00) ; checksum
_start: ; Main function, called first by startup after grub has loaded it.
cli ; clear interrupt flags
mov esp,stack ; set the stack pointer to our stack
push ebx ; push the multiboot structure in the stack
call kernel_main ; calling the kernel_main function TODO: programm your OS!
hlt ; OK, here we reach the end of the OS kernel
; Your TODO will be to poweroff the computer bevore, else
; the computer will stop here the processor, and you can press the
; off button of your computer here
section .bss
resb 32768 ; 32 768 byte stack, you are free to edit the bytes number
stack:
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment