D Bare Bones With LDC2
Jump to navigation
Jump to search
This page or section is a work in progress and may thus be incomplete. Its content may be changed in the near future. |
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 |
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;