D Bare Bones With LDC2
From OSDev Wiki
|
This tutorial needs to explain what the code does as tutorials are not just copy paste. You can help out by editing this page to include more context to what the code does. |
WAIT! Have you read Getting Started, Beginner Mistakes, and some of the related OS theory? |
Difficulty level |
---|
![]() Medium |
Contents |
Requirements
You'll need LDC2, Yasm and ld to compile the kernel.
GRUB, mkisofs and QEMU could be used to test it.
References
Most sources and information come from osdev wiki and Xomb wiki.
http://wiki.xomb.org/index.php?title=XOmB_Bare_Bones
Instructions to build the kernel
yasm -o boot.o boot.s -felf64 yasm -o load.o load.s -felf64 ldc2 -c -nodefaultlib -code-model=large -of=kmain.o kmain.d ld -nostdlib -nodefaultlibs -b elf64-x86-64 -T linker.ld -o kernel.bin boot.o load.o kmain.o
Build iso and test using QEMU
mkisofs -R -b boot/grub/stage2_eltorito -no-emul-boot -boot-load-size 16 \ -boot-info-table -input-charset UTF-8 -o xomb.iso ./iso qemu-system-x86_64 -cdrom xomb.iso
boot.s
; boot.s ; entry is from bootloader section .text bits 32 %include "defines.mac" ; externs given by the linker script extern _edata extern _end ; extern to the load.s extern start64 extern stack ; define the starting point for this module global start global _start start: _start: ; Stash values for multiboot we won't touch until 64 bit mode mov esi, ebx mov edi, eax jmp start32 ; the multiboot header needs to be aligned at ; a 32 bit boundary align 4 multiboot_header: dd MULTIBOOT_HEADER_MAGIC dd MULTIBOOT_HEADER_FLAGS dd -(MULTIBOOT_HEADER_MAGIC + MULTIBOOT_HEADER_FLAGS) dd multiboot_header dd _start dd (_edata-KERNEL_VMA_BASE) dd (_end-KERNEL_VMA_BASE) dd _start ; the 32 bit entry global start32 start32: ; disable interrupts cli ; enable 64-bit page translation table entries ; by setting CR4.PAE = 1. ; ; Paging is not enabled until long mode. mov eax, cr4 bts eax, 5 mov cr4, eax ; Create long mode page table and init CR3 to ; point to the base of the PML4 page table mov eax, pml4_base mov cr3, eax ; Enable Long mode and SYSCALL / SYSRET instructions mov ecx, 0xC0000080 rdmsr bts eax, 8 bts eax, 0 wrmsr ; Load the 32 bit GDT lgdt [pGDT32] ; Load the 32 bit IDT ; lidt [pIDT32] ; establish a stack for 32 bit code mov esp, (stack-KERNEL_VMA_BASE) + STACK_SIZE ; enable paging to activate long mode mov eax, cr0 bts eax, 31 mov cr0, eax jmp CS_KERNEL:(start64-KERNEL_VMA_BASE) bits 64 code64Jump: jmp (start64-KERNEL_VMA_BASE) ; Data Structures Follow bits 32 ; 32 bit gdt align 4096 pGDT32: dw GDT_END - GDT_TABLE - 1 dq GDT_TABLE - KERNEL_VMA_BASE GDT_TABLE: dq 0x0000000000000000 ; Null Descriptor dq 0x00cf9a000000ffff ; CS_KERNEL32 dq 0x00af9a000000ffff,0 ; CS_KERNEL dq 0x00af93000000ffff,0 ; DS_KERNEL dq 0x00affa000000ffff,0 ; CS_USER dq 0x00aff3000000ffff,0 ; DS_USER dq 0,0 ; dq 0,0 ; dq 0,0 ; dq 0,0 ; dq 0,0,0 ; Three TLS descriptors dq 0x0000f40000000000 ; GDT_END: ; Temporary page tables ; These assume linking to 0xFFFF800000000000 align 4096 pml4_base: dq (pml3_base + 0x7) times 255 dq 0 dq (pml3_base + 0x7) times 255 dq 0 align 4096 pml3_base: dq (pml2_base + 0x7) times 511 dq 0 align 4096 pml2_base: %assign i 0 %rep 25 dq (pml1_base + i + 0x7) %assign i i+4096 %endrep times (512-25) dq 0 align 4096 ; 15 tables are described here ; this maps 40 MB from address 0x0 ; to an identity mapping pml1_base: %assign i 0 %rep 512*25 dq (i << 12) | 0x087 %assign i i+1 %endrep
load.s
; load.s ; entry is from boot.s bits 64 ; Everywhere you see some weird addition logic ; This is to fit the addresses into 32 bit sizes ; Note, they will sign extend! section .text ; include useful definitions %include "defines.mac" ; extern to kmain.d extern kmain global start64 start64: ; Initialize the 64 bit stack pointer. mov rsp, ((stack - KERNEL_VMA_BASE) + STACK_SIZE) ; Set up the stack for the return. push CS_KERNEL ; RAX - the address to return to mov rax, KERNEL_VMA_BASE >> 32 shl rax, 32 or rax, long_entry - (KERNEL_VMA_BASE & 0xffffffff00000000) push rax ; Go into canonical higher half ; It uses a trick to update the program counter ; across a 64 bit address space ret long_entry: ; From here on out, we are running instructions ; within the higher half (0xffffffff80000000 ... ) ; We can safely upmap the lower half, we do not ; need an identity mapping of this region ; set up a 64 bit virtual stack mov rax, KERNEL_VMA_BASE >> 32 shl rax, 32 or rax, stack - (KERNEL_VMA_BASE & 0xffffffff00000000) mov rsp, rax ; set cpu flags push 0 lss eax, [rsp] popf ; set the input/output permission level to 3 ; it will allow all access pushf pop rax or rax, 0x3000 push rax popf ; update the multiboot struct to point to a ; virtual address add rsi, (KERNEL_VMA_BASE & 0xffffffff) ; push the parameters (just in case) push rsi push rdi ; call kmain call kmain ; we should not get here haltloop: hlt jmp haltloop nop nop nop ; stack space global stack align 4096 stack: %rep STACK_SIZE dd 0 %endrep
defines.mac
; multiboot definitions %define MULTIBOOT_HEADER_MAGIC 0x1BADB002 %define MULTIBOOT_HEADER_FLAGS 0x00010003 ; where is the kernel? %define KERNEL_VMA_BASE 0xFFFF800000000000 %define KERNEL_LMA_BASE 0x100000 ; the gdt entry to use for the kernel %define CS_KERNEL 0x10 %define CS_KERNEL32 0x08 ; other definitions %define STACK_SIZE 0x4000
kmain.d
module kmaina; extern(C) void kmain(int bootLoaderID, void *data) { int ypos = 0; //Starting points of the cursor int xpos = 0; const uint COLUMNS = 80; //Screensize const uint LINES = 24; ubyte* vidmem = cast(ubyte*)0xFFFF8000000B8000; //Video memory address for (int i = 0; i < COLUMNS * LINES * 2; i++) { //Loops through the screen and clears it *(vidmem + i) = 0; } *(vidmem + (xpos + ypos * COLUMNS) * 2) = 'D' & 0xFF; //Prints the letter D *(vidmem + (xpos + ypos * COLUMNS) * 2 + 1) = 0x07; //Sets the colour for D to be light grey (0x07) for (;;) { //Loop forever. You can add your kernel logic here } } extern(C) void* _Dmodule_ref;