diff --git a/.gitignore b/.gitignore index 76d3b8b..06326bf 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ /*/*.bin /*/*.o /*/*.swp +/*/*.dis diff --git a/07-bootsector-disk/README.md b/07-bootsector-disk/README.md index 76f000d..1727c7f 100644 --- a/07-bootsector-disk/README.md +++ b/07-bootsector-disk/README.md @@ -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: diff --git a/13-kernel-barebones/Makefile b/13-kernel-barebones/Makefile new file mode 100644 index 0000000..2882854 --- /dev/null +++ b/13-kernel-barebones/Makefile @@ -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 diff --git a/13-kernel-barebones/README.md b/13-kernel-barebones/README.md new file mode 100644 index 0000000..23aa38d --- /dev/null +++ b/13-kernel-barebones/README.md @@ -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. diff --git a/13-kernel-barebones/bootsect.asm b/13-kernel-barebones/bootsect.asm new file mode 100644 index 0000000..3358a58 --- /dev/null +++ b/13-kernel-barebones/bootsect.asm @@ -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 diff --git a/13-kernel-barebones/kernel.c b/13-kernel-barebones/kernel.c new file mode 100644 index 0000000..bcdeeb2 --- /dev/null +++ b/13-kernel-barebones/kernel.c @@ -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'; +} diff --git a/13-kernel-barebones/kernel_entry.asm b/13-kernel-barebones/kernel_entry.asm new file mode 100644 index 0000000..e38cf9a --- /dev/null +++ b/13-kernel-barebones/kernel_entry.asm @@ -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 $