Skip to content

Commit

Permalink
lesson 13, first kernel
Browse files Browse the repository at this point in the history
  • Loading branch information
cfenollosa committed Oct 15, 2014
1 parent 14f51e8 commit 2f1378d
Show file tree
Hide file tree
Showing 7 changed files with 180 additions and 1 deletion.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/*/*.bin
/*/*.o
/*/*.swp
/*/*.dis
2 changes: 1 addition & 1 deletion 07-bootsector-disk/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ read it.
is booting from the right drive and set the drive on `dl` accordingly**

The BIOS sets `dl` to the drive number before calling the bootloader. However,
I found some problems with qemu then booting from the hdd.
I found some problems with qemu when booting from the hdd.

There are two quick options:

Expand Down
32 changes: 32 additions & 0 deletions 13-kernel-barebones/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# $@ = target file
# $< = first dependency
# $^ = all dependencies

# First rule is the one executed when no paramaters are fed to the Makefile
all: run

# Notice how dependencies are built as needed
kernel.bin: kernel_entry.o kernel.o
i386-elf-ld -o $@ -Ttext 0x1000 $^ --oformat binary

kernel_entry.o: kernel_entry.asm
nasm $< -f elf -o $@

kernel.o: kernel.c
i386-elf-gcc -ffreestanding -c $< -o $@

# Rule to disassemble the kernel - may be useful to debug
kernel.dis: kernel.bin
ndisasm -b 32 $< > $@

bootsect.bin: bootsect.asm
nasm $< -f bin -o $@

os-image.bin: bootsect.bin kernel.bin
cat $^ > os-image.bin

run: os-image.bin
qemu-system-i386 -fda $<

clean:
rm *.bin *.o *.dis
84 changes: 84 additions & 0 deletions 13-kernel-barebones/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
*Concepts you may want to Google beforehand: kernel, ELF format, makefile*

**Goal: Create a simple kernel and a bootsector capable of booting it**

The kernel
----------

Our C kernel will just print an 'X' on the top left corner of the screen. Go ahead
and open `kernel.c`.

You will notice a dummy function that does nothing. That function will force us
to create a kernel entry routine which does not point to byte 0x0 in our kernel, but
to an actual label which we know that launches it. In our case, function `main()`.

`i386-elf-gcc -ffreestanding -c kernel.c -o kernel.o`

That routine is coded on `kernel_entry.asm`. Read it and you will learn how to
use `[extern]` declarations in assembly. To compile this file, instead of generating
a binary, we will generate an `elf` format file which will be linked with `kernel.o`

`nasm kernel_entry.asm -f elf -o kernel_entry.o`


The linker
----------

A linker is a very powerful tool and we only started to benefit from it.

To link both object files into a single binary kernel and resolve label references,
run:

`i386-elf-ld -o kernel.bin -Ttext 0x1000 kernel_entry.o kernel.o --oformat binary`

Notice how our kernel will be placed not at `0x0` in memory, but at `0x1000`. The
bootsector will need to know this address too.


The bootsector
--------------

It is very similar to the one in lesson 10. Open `bootsect.asm` and examine the code.
Actually, if you remove all the lines used to print messages on the screen, it accounts
to a couple dozen lines.

Compile it with `nasm bootsect.asm -f bin -o bootsect.bin`


Putting it all together
-----------------------

Now what? We have two separate files for the bootsector and the kernel?

Can't we just "link" them together into a single file? Yes, we can, and it's easy,
just concatenate them:

`cat bootsect.bin kernel.bin > os-image.bin`


Run!
----

You can now run `os-image.bin` with qemu.

Remember that if you find disk load errors you may need to play with the disk numbers
or qemu parameters (floppy = `0x0`, hdd = `0x80`). I usually use

`qemu-system-i386 -fda os-image.bin`

You will see four messages:

- "Started in 16-bit Real Mode"
- "Loading kernel into memory"
- (Top left) "Landed in 32-bit Protected Mode"
- (Top left, overwriting previous message) "X"

Congratulations!


Makefile
--------

As a last step, we will tidy up the compilation process with a Makefile. Open the `Makefile`
script and examine its contents. If you don't know what a Makefile is, now is a good time
to Google and learn it, as this will save us a lot of time in the future.
50 changes: 50 additions & 0 deletions 13-kernel-barebones/bootsect.asm
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
[org 0x7c00]
KERNEL_OFFSET equ 0x1000 ; The same one we used when linking the kernel

mov [BOOT_DRIVE], dl ; Remember that the BIOS sets us the boot drive in 'dl' on boot
mov bp, 0x9000
mov sp, bp

mov bx, MSG_REAL_MODE
call print
call print_nl

call load_kernel ; read the kernel from disk
call switch_to_pm ; disable interrupts, load GDT, etc. Finally jumps to 'BEGIN_PM'
jmp $ ; Never executed

%include "../05-bootsector-functions-strings/boot_sect_print.asm"
%include "../05-bootsector-functions-strings/boot_sect_print_hex.asm"
%include "../07-bootsector-disk/boot_sect_disk.asm"
%include "../09-32bit-gdt/32bit-gdt.asm"
%include "../08-32bit-print/32bit-print.asm"
%include "../10-32bit-enter/32bit-switch.asm"

[bits 16]
load_kernel:
mov bx, MSG_LOAD_KERNEL
call print
call print_nl

mov bx, KERNEL_OFFSET ; Read from disk and store in 0x1000
mov dh, 2
mov dl, [BOOT_DRIVE]
call disk_load
ret

[bits 32]
BEGIN_PM:
mov ebx, MSG_PROT_MODE
call print_string_pm
call KERNEL_OFFSET ; Give control to the kernel
jmp $ ; Stay here when the kernel returns control to us (if ever)


BOOT_DRIVE db 0 ; It is a good idea to store it in memory because 'dl' may get overwritten
MSG_REAL_MODE db "Started in 16-bit Real Mode", 0
MSG_PROT_MODE db "Landed in 32-bit Protected Mode", 0
MSG_LOAD_KERNEL db "Loading kernel into memory", 0

; padding
times 510 - ($-$$) db 0
dw 0xaa55
8 changes: 8 additions & 0 deletions 13-kernel-barebones/kernel.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/* This will force us to create a kernel entry function */
void dummy_test_entrypoint() {
}

void main() {
char* video_memory = (char*) 0xb8000;
*video_memory = 'X';
}
4 changes: 4 additions & 0 deletions 13-kernel-barebones/kernel_entry.asm
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
[bits 32]
[extern main] ; Define calling point. Must haveSame name as kernel.c 'main' function
call main ; Calls the C function. The linker will know where it is placed in memory
jmp $

0 comments on commit 2f1378d

Please sign in to comment.