Explorar o código

lesson 13, first kernel

Carlos Fenollosa %!s(int64=10) %!d(string=hai) anos
pai
achega
2f1378d7a3

+ 1 - 0
.gitignore

@@ -1,3 +1,4 @@
 /*/*.bin
 /*/*.bin
 /*/*.o
 /*/*.o
 /*/*.swp
 /*/*.swp
+/*/*.dis

+ 1 - 1
07-bootsector-disk/README.md

@@ -51,7 +51,7 @@ read it.
 is booting from the right drive and set the drive on `dl` accordingly**
 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,
 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:
 There are two quick options:
 
 

+ 32 - 0
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

+ 84 - 0
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.

+ 50 - 0
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

+ 8 - 0
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';
+}

+ 4 - 0
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 $