mirror of
https://github.com/cfenollosa/os-tutorial.git
synced 2024-10-27 20:34:19 +00:00
lesson 13, first kernel
This commit is contained in:
parent
14f51e8246
commit
2f1378d7a3
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,3 +1,4 @@
|
||||
/*/*.bin
|
||||
/*/*.o
|
||||
/*/*.swp
|
||||
/*/*.dis
|
||||
|
@ -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:
|
||||
|
||||
|
32
13-kernel-barebones/Makefile
Normal file
32
13-kernel-barebones/Makefile
Normal file
@ -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
13-kernel-barebones/README.md
Normal file
84
13-kernel-barebones/README.md
Normal file
@ -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
13-kernel-barebones/bootsect.asm
Normal file
50
13-kernel-barebones/bootsect.asm
Normal file
@ -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
13-kernel-barebones/kernel.c
Normal file
8
13-kernel-barebones/kernel.c
Normal file
@ -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
13-kernel-barebones/kernel_entry.asm
Normal file
4
13-kernel-barebones/kernel_entry.asm
Normal file
@ -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 $
|
Loading…
Reference in New Issue
Block a user