summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAiden Gall <aiden@aidengall.xyz>2024-05-14 19:43:28 +0100
committerAiden Gall <aiden@aidengall.xyz>2024-05-14 19:51:05 +0100
commit93585dc4da3be099e1ffe7e757aa7caff2e1f013 (patch)
treec1335acdd74cff12e545ce386ab2dbd176eaa473
initial commit
-rw-r--r--Makefile36
-rw-r--r--boot/bios.inc128
-rw-r--r--boot/boot.asm144
-rw-r--r--boot/page.inc44
-rw-r--r--boot/segdesc.inc40
-rw-r--r--kernel/main.c83
-rw-r--r--kernel/util.asm20
-rw-r--r--kernel/util.h2
-rw-r--r--link.ld39
9 files changed, 536 insertions, 0 deletions
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..0dbfe31
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,36 @@
+.POSIX:
+.PHONY: all clean run
+
+# QFLAGS = -machine type=pc,accel=kvm -cpu Broadwell-v3
+
+CC = x86_64-elf-gcc
+CFLAGS = -Wall -Wextra -std=c99 -pedantic -O2 -pipe \
+ -ffreestanding -mno-red-zone -mno-mmx -mno-sse -mno-sse2
+LDFLAGS = -Wl,-O1 -T link.ld
+LDLIBS = -lgcc -nostdlib
+
+CSRC = kernel/main.c
+FSRC = boot/boot.asm kernel/util.asm
+OBJ = ${CSRC:.c=.o} ${FSRC:.asm=.o}
+
+all: providence.img
+
+boot/boot.o: boot/bios.inc boot/page.inc boot/segdesc.inc
+kernel/main.o: kernel/util.h
+
+providence.img: ${OBJ}
+ ${CC} ${LDFLAGS} $^ ${LDLIBS} -o $@
+ @du -b $@
+ truncate -s 29696 $@
+
+%.o: %.c
+ ${CC} ${CFLAGS} -c -o $@ $<
+
+%.o: %.asm
+ fasm $< $@
+
+clean:
+ rm -f ${OBJ} providence.img
+
+run: all
+ qemu-system-x86_64 ${QFLAGS} -drive file=providence.img,format=raw,index=0,media=disk
diff --git a/boot/bios.inc b/boot/bios.inc
new file mode 100644
index 0000000..ccc94e3
--- /dev/null
+++ b/boot/bios.inc
@@ -0,0 +1,128 @@
+macro bios_a20_enable {
+ mov ax, 0x2401
+ int 0x15
+}
+
+macro bios_vga_write_mode_3 {
+ mov ax, 0x03
+ int 0x10
+}
+
+; assumes dl = drive number
+macro bios_disk_read dst*, cyl*, head*, sector*, count*, err_label* {
+ local read_disk
+
+ assert 0 <= cyl & cyl <= 1023
+ assert 0 <= head & head <= 15
+ assert 1 <= sector & sector <= 17
+ assert 1 <= count & count <= 128
+
+ ; try to read disk up to 3 times
+ mov di, 3
+
+read_disk:
+ ; ah = 0x02, al = number of sectors to read
+ mov ax, (0x0200 or count)
+
+ ; es:bx = pointer to destination buffer
+ if dst = 0
+ xor bx, bx
+ else
+ mov bx, dst
+ end if
+
+ ; ch = track/cylinder number, cl = sector number
+ mov cx, ((cyl and 0xff) shl 8) or ((cyl and 0x300) shl 6) or sector
+
+ ; dh = head number
+ if head = 0
+ xor dh, dh
+ else
+ mov dh, head
+ end if
+
+ int 0x13
+
+ jc .error
+ cmp ax, count
+ jne .error
+
+ jmp .exit
+.error:
+ dec di
+ jz err_label
+
+ ; reset disk on error
+ xor ah, ah
+ stc
+ int 0x13
+ jc err_label
+ jmp read_disk
+.exit:
+}
+
+macro bios_e820_mmap mmap*, err_label* {
+ local e820, e820_entry
+e820:
+ virtual at di
+ label e820_entry
+ .base dq ?
+ .length dq ?
+ .type dd ?
+ .ext_attr dd ?
+ .sizeof = $ - e820_entry
+ end virtual
+
+ mov di, mmap + 4
+ xor ebx, ebx
+ xor bp, bp
+
+ mov edx, 0x0534d4150
+ mov eax, 0xe820
+ mov byte [.ext_attr], 0x01
+ mov ecx, .sizeof
+ int 0x15
+ jc err_label
+
+ mov edx, 0x0534d4150
+ cmp eax, edx
+ jne err_label
+
+ test ebx, ebx
+ jz err_label
+
+ jmp @f
+
+.e820_loop:
+ mov ax, 0xe820
+ mov byte [.ext_attr], 0x01
+ mov ecx, .sizeof
+ int 0x15
+ jc err_label
+
+ mov edx, 0x0534d4150
+@@:
+ jcxz .skip_entry
+
+ ; ACPI < 3.0
+ cmp cl, (.sizeof-4)
+ jbe .not_ext
+
+ ; ignore data flag
+ test byte [.ext_attr], 0x01
+ jz .skip_entry
+
+.not_ext:
+ mov ecx, dword [.length]
+ or ecx, dword [.length + 4]
+ jz .skip_entry
+
+ inc bp
+ add di, .sizeof
+.skip_entry:
+ test ebx, ebx
+ jnz .e820_loop
+
+ ; store e820 mmap entry count at beginning
+ mov [mmap], bp
+}
diff --git a/boot/boot.asm b/boot/boot.asm
new file mode 100644
index 0000000..eedde07
--- /dev/null
+++ b/boot/boot.asm
@@ -0,0 +1,144 @@
+format ELF64
+
+PML4_TABLE = 0x1000
+E820_MMAP = 0xf000
+
+MSR_EFER = 0xc0000080
+
+include 'bios.inc'
+include 'page.inc'
+include 'segdesc.inc'
+
+section '.text.boot' executable
+
+org 0x7c00
+use16
+ jmp far 0x0000:@f ; ensure cs = 0x0000
+@@:
+ xor ax, ax
+ mov ds, ax
+ mov es, ax
+
+ ; put stack directly bellow bootloader
+ mov ss, ax
+ mov sp, $$
+ cld
+
+ mov [boot_disk], dl
+
+ bios_disk_read ($$+0x200), 0, 0, 2, 57, disk_error
+
+ bios_e820_mmap E820_MMAP, e820_error
+
+ bios_a20_enable
+ bios_vga_write_mode_3
+
+ page_table_init_real PML4_TABLE
+
+ ; disable IRQs
+ mov al, 0xff
+ out 0xa1, al
+ out 0x21, al
+
+ lidt [dummy_idtr]
+
+ ; enable PAE (1<<5) and PGE (1<<7)
+ mov eax, (1 shl 7) or (1 shl 5)
+ mov cr4, eax
+
+ ; point cr3 at PML4
+ mov edx, edi
+ mov cr3, edx
+
+ ; enable long mode
+ mov ecx, MSR_EFER
+ rdmsr
+ or eax, (1 shl 8)
+ wrmsr
+
+ ; enable protected mode
+ mov ebx, cr0
+ or ebx, (1 shl 31) or (1 shl 0)
+ mov cr0, ebx
+
+ lgdt [gdtr]
+
+ jmp far (gdt.kcode-gdt):longmode
+
+e820_error:
+ ; change beginning of error message ("disk" -> "e820")
+ mov dword [errmsg], 0x30323865
+disk_error:
+ mov si, errmsg
+ mov ah, 0x0e
+ jmp @f
+.loop:
+ int 0x10
+@@:
+ lodsb
+ test al, al
+ jnz .loop
+
+ ; wait until keypress, then jump to reset vector (reboot)
+ xor ah, ah
+ int 0x16
+ jmp far 0xffff:0x0000
+
+use64
+longmode:
+ mov ax, (gdt.kdata-gdt)
+ mov ds, ax
+ mov es, ax
+ mov ss, ax
+
+ cli
+
+ ; copy kernel to 0x100000
+ mov ecx, (57*0x200)/8
+ mov esi, 0x7e00
+ mov edi, 0x100000
+ rep movsq
+
+ xor eax, eax
+ xor ebx, ebx
+ xor ecx, ecx
+ xor edx, edx
+ xor edi, edi
+
+ ; put stack at top of addressable memory
+ mov esp, 0x200000
+ mov ebp, esp
+
+ jmp 0x100000
+
+align 4
+dw 0
+label gdtr fword
+ .limit dw (gdt.sizeof - 1)
+ .addr dd gdt
+
+align 4
+errmsg db "disk error: press any key to reboot",0
+
+align 8
+gdt:
+ .null dq 0
+ .kcode seg_desc 0, 0, (GDT_CODE_64 or SEG_PRIV0)
+ .kdata seg_desc 0, 0, (GDT_DATA_64 or SEG_PRIV0)
+ .ucode seg_desc 0, 0, (GDT_CODE_64 or SEG_PRIV3)
+ .udata seg_desc 0, 0, (GDT_DATA_64 or SEG_PRIV3)
+ .sizeof = $ - gdt
+
+align 4
+boot_disk db 0, 0
+label dummy_idtr fword
+ .limit dw 0
+ .addr dd 0
+
+if ($-$$) <= 510
+ db 510-($-$$) dup 0
+else
+ display "ERR: boot sector image exceeds 512 bytes",10
+ assert ($-$$) <= 510
+end if
+db 0x55,0xaa
diff --git a/boot/page.inc b/boot/page.inc
new file mode 100644
index 0000000..ec2a8ad
--- /dev/null
+++ b/boot/page.inc
@@ -0,0 +1,44 @@
+PAGE_SIZE = 0x1000
+
+PAGE_PRESENT = (1 shl 0)
+PAGE_WRITE = (1 shl 1)
+
+macro page_table_init_real pml4_table* {
+ local page_table
+
+ ; zero out page table
+ mov edi, pml4_table
+ push di
+ mov ecx, (PAGE_SIZE*4)/4
+ xor eax, eax
+ cld
+ rep stosd
+ pop di
+
+ virtual at di
+ label page_table
+ .PML4T rb PAGE_SIZE
+ .PDPT rb PAGE_SIZE
+ .PDT rb PAGE_SIZE
+ .PT rb PAGE_SIZE
+ .sizeof = $ - page_table
+ end virtual
+
+ ; create first entry
+ mov word [.PML4T], (pml4_table + .PDPT - page_table) or PAGE_PRESENT or PAGE_WRITE
+ mov word [.PDPT], (pml4_table + .PDT - page_table) or PAGE_PRESENT or PAGE_WRITE
+ mov word [.PDT], (pml4_table + .PT - page_table) or PAGE_PRESENT or PAGE_WRITE
+
+ ; identity map pages
+ push di
+ mov di, (pml4_table + .PT - page_table)
+ mov ax, (PAGE_PRESENT or PAGE_WRITE)
+@@:
+ mov [di], eax
+ add eax, PAGE_SIZE
+ add di, 8
+ cmp di, pml4_table + .sizeof
+ jb @b
+
+ pop di
+}
diff --git a/boot/segdesc.inc b/boot/segdesc.inc
new file mode 100644
index 0000000..b4d14dc
--- /dev/null
+++ b/boot/segdesc.inc
@@ -0,0 +1,40 @@
+; segment type
+SEG_ACCESSED = 0001b
+SEG_CODE_READ = 0010b
+SEG_DATA_WRITE = 0010b
+SEG_CODE_CONFORM = 0100b
+SEG_DATA_EXPDOWN = 0100b
+SEG_EXECUTE = 1000b
+
+; privilege level
+SEG_PRIV0 = (00b shl 0x05)
+SEG_PRIV1 = (01b shl 0x05)
+SEG_PRIV2 = (10b shl 0x05)
+SEG_PRIV3 = (11b shl 0x05)
+
+; other flags
+SEG_DESCTYPE = (1b shl 0x04) ; descriptor type
+SEG_PRES = (1b shl 0x07) ; present
+SEG_SAVL = (1b shl 0x0C) ; available for system use
+SEG_LONG = (1b shl 0x0D) ; long mode
+SEG_SIZE = (1b shl 0x0E) ; size
+SEG_GRAN = (1b shl 0x0F) ; granularity
+
+; segment descriptor flag sets for convenience
+GDT_CODE_32 = SEG_DESCTYPE or SEG_PRES or SEG_SIZE or SEG_GRAN or SEG_EXECUTE or SEG_CODE_READ
+GDT_DATA_32 = SEG_DESCTYPE or SEG_PRES or SEG_SIZE or SEG_GRAN or SEG_DATA_WRITE
+GDT_CODE_64 = SEG_DESCTYPE or SEG_PRES or SEG_LONG or SEG_EXECUTE or SEG_CODE_READ
+GDT_DATA_64 = SEG_DESCTYPE or SEG_PRES or SEG_LONG or SEG_DATA_WRITE
+
+struc seg_desc base*, limit*, flag* {
+ assert 0x0 <= limit & limit <= 0x000fffff
+ assert 0x0 <= base & base <= 0xffffffff
+ assert (flag or 0xf0ff) = 0xf0ff
+
+ . dq ((limit shl 0x20) and 0x000f000000000000) or \
+ ((flag shl 0x28) and 0x00f0ff0000000000) or \
+ ((base shl 0x10) and 0x000000ff00000000) or \
+ ((base shl 0x20) and 0xff00000000000000) or \
+ ((base shl 0x10) and 0x00000000ffff0000) or \
+ ((limit shl 0x00) and 0x000000000000ffff)
+}
diff --git a/kernel/main.c b/kernel/main.c
new file mode 100644
index 0000000..e69af30
--- /dev/null
+++ b/kernel/main.c
@@ -0,0 +1,83 @@
+#include <stdint.h>
+#include "util.h"
+
+#define VGA_WIDTH 80
+#define VGA_HEIGHT 25
+
+struct e820_entry {
+ uint64_t base;
+ uint64_t length;
+ uint32_t type;
+ uint32_t ext_attr;
+};
+
+static uint64_t *
+memsetu64(uint64_t *const dst, const uint64_t c, const uint64_t n)
+{
+ for (uint64_t i = 0; i < n; ++i) dst[i] = c;
+ return dst;
+}
+
+static uint16_t *
+print_hex(uint16_t *dst, uint64_t num)
+{
+ dst[0] = 0x0f00 | '0';
+ dst[1] = 0x0f00 | 'x';
+
+ dst += 2;
+
+ for (int i = 0; i < 16; ++i) {
+ uint16_t x = (num >> 60);
+
+ *(dst++) = 0x0f00 | (x < 0xa ? x+'0' : x+'a'-0xa);
+
+ num <<= 4;
+ }
+
+ return dst;
+}
+
+static uint16_t *
+puts(uint16_t *dst, const char *str)
+{
+ for (; *str; ++str, ++dst) *dst = 0x0f00|(*str);
+ return dst;
+}
+
+static uint16_t *
+print_e820_table(uint16_t *dst, const struct e820_entry *const entries, const uint32_t count)
+{
+ for (uint32_t i = 0; i < count; ++i, dst += VGA_WIDTH) {
+ uint16_t *cursor;
+
+ cursor = print_hex(dst, entries[i].base);
+
+ *(cursor++) = 0x0f00 | '-';
+
+ cursor = print_hex(cursor, entries[i].base + entries[i].length - 1);
+
+ ++cursor;
+ cursor = puts(cursor, entries[i].type == 1 ? "available" : "reserved");
+ }
+
+ return dst;
+}
+
+void
+kmain(void)
+{
+ vga_cursor_hide();
+
+ uint16_t *const vga_memory = (uint16_t *)0xb8000;
+ memsetu64((uint64_t *)vga_memory, 0x0f200f200f200f20, (VGA_WIDTH*VGA_HEIGHT*sizeof(uint16_t))/sizeof(uint64_t));
+
+ puts(vga_memory, "ProvidenceOS v0.1");
+ puts(vga_memory+VGA_WIDTH*2, "e820 memory map:");
+
+ const uint32_t e820_size = *(uint32_t *)0xf000;
+ const struct e820_entry *const e820_mmap = (struct e820_entry *)0xf004;
+
+ print_e820_table(vga_memory+(VGA_WIDTH*3), e820_mmap, e820_size);
+
+ halt();
+}
diff --git a/kernel/util.asm b/kernel/util.asm
new file mode 100644
index 0000000..548223d
--- /dev/null
+++ b/kernel/util.asm
@@ -0,0 +1,20 @@
+format ELF64
+
+section '.text' executable
+
+public vga_cursor_hide
+vga_cursor_hide:
+ mov dx, 0x03d4
+ mov al, 0x0a
+ out dx, al
+
+ inc dx
+ mov al, 0x20
+ out dx, al
+
+ ret
+
+public halt
+halt:
+ hlt
+ jmp halt
diff --git a/kernel/util.h b/kernel/util.h
new file mode 100644
index 0000000..1015c9c
--- /dev/null
+++ b/kernel/util.h
@@ -0,0 +1,2 @@
+void vga_cursor_hide(void);
+void halt(void);
diff --git a/link.ld b/link.ld
new file mode 100644
index 0000000..33b6066
--- /dev/null
+++ b/link.ld
@@ -0,0 +1,39 @@
+ENTRY(kmain)
+OUTPUT_FORMAT(binary)
+
+KERNEL_VMA = 0x100000;
+
+SECTIONS {
+ . = 0x7c00;
+
+ .text.boot : {
+ boot/boot.o (.text.boot)
+ _boot_end = .;
+ }
+
+ .text KERNEL_VMA : AT(_boot_end) {
+ *(.text.startup)
+ *(.text)
+ *(.rodata*)
+ . = ALIGN(0x1000);
+ }
+
+ .data : {
+ *(.data)
+ . = ALIGN(0x1000);
+ }
+
+ .eh_frame : {
+ *(.eh_frame)
+ . = ALIGN(0x1000);
+ }
+
+ .bss : {
+ *(.bss)
+ . = ALIGN(0x1000);
+ }
+
+ /DISCARD/ : {
+ *(.comment)
+ }
+}