D Bare Bones With LDC2

From OSDev Wiki
Jump to navigation Jump to search

This page is under construction! 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
Difficulty 2.png
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;