diff options
author | Aiden Gall <aiden@aidengall.xyz> | 2024-05-14 19:43:28 +0100 |
---|---|---|
committer | Aiden Gall <aiden@aidengall.xyz> | 2024-05-14 19:51:05 +0100 |
commit | 93585dc4da3be099e1ffe7e757aa7caff2e1f013 (patch) | |
tree | c1335acdd74cff12e545ce386ab2dbd176eaa473 |
initial commit
-rw-r--r-- | Makefile | 36 | ||||
-rw-r--r-- | boot/bios.inc | 128 | ||||
-rw-r--r-- | boot/boot.asm | 144 | ||||
-rw-r--r-- | boot/page.inc | 44 | ||||
-rw-r--r-- | boot/segdesc.inc | 40 | ||||
-rw-r--r-- | kernel/main.c | 83 | ||||
-rw-r--r-- | kernel/util.asm | 20 | ||||
-rw-r--r-- | kernel/util.h | 2 | ||||
-rw-r--r-- | link.ld | 39 |
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); @@ -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) + } +} |