diff --git a/.ccls b/.ccls new file mode 100644 index 0000000..7713598 --- /dev/null +++ b/.ccls @@ -0,0 +1,2 @@ +clang +-Isrc/include diff --git a/Makefile b/Makefile index 05874c2..b5611d9 100644 --- a/Makefile +++ b/Makefile @@ -9,15 +9,17 @@ CFLAGS?= ASFLAGS?= # Define flags for C files + CFLAGS:=\ -O2 \ --std=gnu99 \ -ffreestanding \ -Wall -Wextra \ --Wno-incompatible-pointer-types\ +-fno-exceptions -fno-rtti \ -I$(INCLUDEDIR) \ $(CFLAGS) +CXXFLAGS:=$(CFLAGS) + # Define flags for assembly files ASFLAGS:=\ -I$(SOURCEIDR)$(INCLUDEDIR) \ @@ -31,7 +33,10 @@ LINKERFLAGS:=\ -lgcc LIB_OBJS=\ -$(BUILDDIR)$(ARCHDIR)bootstrap.o +$(BUILDDIR)$(ARCHDIR)bootstrap.o \ +$(BUILDDIR)$(ARCHDIR)paging.o \ +$(BUILDDIR)$(ARCHDIR)initialize_pages.o \ +$(BUILDDIR)$(ARCHDIR)tty.o KERNEL_OBJS=\ $(BUILDDIR)kernel/kernel.o @@ -43,6 +48,9 @@ LINKER_PATH=$(SOURCEDIR)$(ARCHDIR)linker.ld .PHONY: all clean snowstorm.iso debug .SUFFIXES: .c .o .s +$(BUILDDIR)%.o : $(SOURCEDIR)%.cpp + i686-elf-g++ -c $< -o $@ $(CXXFLAGS) + $(BUILDDIR)%.o : $(SOURCEDIR)%.c i686-elf-gcc -c $< -o $@ $(CFLAGS) @@ -61,11 +69,11 @@ snowstorm.kernel: $(OBJS) i686-elf-gcc -T $(LINKER_PATH) -o build/snowstorm.kernel $(OBJS) $(LINKERFLAGS) grub-file --is-x86-multiboot build/snowstorm.kernel -debug: CFLAGS += -g -O0 +debug: CXXFLAGS += -g -O1 debug: ASFLAGS += -g +debug: CFLAGS += -g -O1 debug: all - clean: rm -f $(OBJS) rm -f build/snowstorm.kernel @@ -76,7 +84,5 @@ run: all qemu-system-i386 -d int -no-reboot -kernel build/snowstorm.kernel run-debug: debug - qemu-system-i386 -d int -no-reboot -S -gdb tcp::9000 build/snowstorm.iso - -run-debug-kernel: debug qemu-system-i386 -d int -no-reboot -S -gdb tcp::9000 -kernel build/snowstorm.kernel + diff --git a/src/arch/i686/bootstrap.s b/src/arch/i686/bootstrap.s index e5eb614..007d309 100644 --- a/src/arch/i686/bootstrap.s +++ b/src/arch/i686/bootstrap.s @@ -12,26 +12,20 @@ .long FLAGS .long CHECKSUM -.section .bss, "aw", @nobits +.section .multiboot.bss, "aw", @nobits .align 16 stack_bottom: .skip (1 << 16) # 64 KiB stack_top: -.global boot_page_directory - .align 4096 -boot_page_directory: - .skip 4096 -boot_page_table1: - .skip 4096 - .section .multiboot.text, "a" .global _start .type _start, @function -.extern kernel_main _start: mov $stack_top, %esp # The stack grows downwards so it starts at the top - # (We kind of need to have a stack to do anything) + # (We kind of need to have a stack to do anything in a high-level language) + + call initialize_pages call kernel_main diff --git a/src/arch/i686/initialize_pages.c b/src/arch/i686/initialize_pages.c new file mode 100644 index 0000000..850c5f8 --- /dev/null +++ b/src/arch/i686/initialize_pages.c @@ -0,0 +1,74 @@ +#include +#include +#include "units.h" + +struct PageDirectoryEntry { + bool present : 1; + bool write : 1; + bool user : 1; + bool write_through : 1; + bool cache_disabled : 1; + bool accessed : 1; + bool available_2 : 1; + bool size : 1; + size_t available_1 : 4; + size_t page_table_address : 20; +} __attribute__((packed)); // Force the compiler to keep our structure intact. + +struct PageTableEntry { + bool present : 1; + bool write : 1; + bool user : 1; + bool write_through : 1; + bool cache_disabled : 1; + bool accessed : 1; + bool dirty : 1; + bool pat : 1; + bool global : 1; + size_t available : 3; + size_t physical_address : 20; +} __attribute__((packed)); + +extern const size_t _kernel_start; +extern const size_t _kernel_end; + +struct PageDirectoryEntry _page_directory[1024] __attribute__((aligned(4096))); // Create a page directory with 1024 entries and make sure it's 4 KiB aligned + +// Identity page table for lowest 4 MiB, including this +struct PageTableEntry _identity_page_table[1024] __attribute__((aligned(4096))); + +// Assumes kernel is smaller than 4 MiB, should change later +struct PageTableEntry _kernel_page_table[1024] __attribute__((aligned(4096))); + +void initialize_pages() { + struct PageDirectoryEntry identity_pd_entry = { + .page_table_address = (size_t)(_identity_page_table) >> 12, + .size = false, // Small (4 KiB) Pages + .user = false, // Ring 3 can't access + .write = true, // Write enabled + .present = true, + }; + + _page_directory[0] = identity_pd_entry; + + // Identity page lowest 4 MiB to not crash instantly + for (size_t i = 0; i < mib(4) / kib(4); i++) { + struct PageTableEntry table_entry = { + .physical_address = i, + .user = false, + .write = true, + .present = true, + }; + _identity_page_table[i] = table_entry; + } + + _page_directory[768] = identity_pd_entry; + + asm("mov %0, %%cr3" :: "r" (_page_directory)); + uint32_t control_register_0; + asm("mov %%cr0, %0" : "=r" (control_register_0)); + // Enable the paging bit + control_register_0 = control_register_0 | 0x80000000; + asm("mov %0, %%cr0" :: "r" (control_register_0)); +} + diff --git a/src/arch/i686/linker.ld b/src/arch/i686/linker.ld index 66bf135..9ffe1d0 100644 --- a/src/arch/i686/linker.ld +++ b/src/arch/i686/linker.ld @@ -3,27 +3,55 @@ ENTRY(_start) SECTIONS { - /* Leave 1 Megabyte for GRUB */ - . = 1M; - + /* Leave 2 Mibibytes for GRUB */ + . = 2M; + .multiboot.data : { *(.multiboot.data) /* Take the data from label .multiboot.data from all files and put it in our output file */ } - .multiboot.text : + .multiboot.text BLOCK(4K) : ALIGN(4K) { - *(.multiboot.text) /* Take the data from label .multiboot.data from all files and put it in our output file */ + *(.multiboot.text) + } + + .multiboot.bss : + { + *(.multiboot.bss) /* Take the data from label .multiboot.data from all files and put it in our output file */ } - .bss : - { - *(.bss) /* Take the data from label .multiboot.data from all files and put it in our output file */ + .pages.bss : ALIGN(4K) { + *initialize_pages.o(.bss) } - .data : - { - *(.data) /* Take the data from label .multiboot.data from all files and put it in our output file */ + .pages.data : ALIGN(4K) { + *initialize_pages.o(.data) } + .pages.text : ALIGN(4K) { + *initialize_pages.o(.text) + } + + _kernel_start = .; + + /* Higher half kernel */ + . += 0xC0000000; + + .data BLOCK(4K) : AT (ADDR (.data) - 0xC0000000) + { + EXCLUDE_FILE (*initialize_pages.o) *(.data) + } + + .bss BLOCK(4K) : AT (ADDR (.bss) - 0xC0000000) + { + EXCLUDE_FILE (*initialize_pages.o) *(.bss) + } + + .text BLOCK(4K) : AT (ADDR (.text) - 0xC0000000) + { + EXCLUDE_FILE (*initialize_pages.o) *(.text) + } + + _kernel_end = .; } diff --git a/src/arch/i686/paging.cpp b/src/arch/i686/paging.cpp new file mode 100644 index 0000000..9fa3ac5 --- /dev/null +++ b/src/arch/i686/paging.cpp @@ -0,0 +1,123 @@ +#include +#include "arch/paging.hpp" +#include "units.h" + +/* +* When doing paging on x86, we need to keep track of a few things. +* The "Page Directory" is a 4 KiB aligned table containing 1024 entries, each of which point to a "Page Table", which also is a 4 KiB table with 1024 entries where each entry points to a 4 KiB page. +* When accessing virtual memory, the MMU will take the first 10 bits to index into the page directory and lookup the corresponding page table. +* It will then use the next 10 bits to index into the page table. Then it will use the last 12 bits as an offset into the page, returning that byte. +* (And a lot of complicated other caching stuff that we don't need to worry to much about) +* +* Paging is very important since every program wants the same region of memory, and paging makes them think they get the memory that they want. +* +*/ + +/* +* +* I want to both have a nice user interface, but also a bit of low-level control for the rest of the kernel +* +* +* +*/ + +#define NUM_PAGES 32768 + +// Make a list of 32768 pages, representing 128 MiB of physical memory +Page pages[NUM_PAGES]; + +struct PageDirectoryEntry { + uint32_t page_table_address : 20; + uint32_t available_1 : 4; + bool size : 1; + bool available_2 : 1; + bool accessed : 1; + bool cache_disabled : 1; + bool write_through : 1; + bool user : 1; + bool write : 1; + bool present : 1; +} __attribute__((packed)); // Force the compiler to keep our structure intact. + +struct PageTableEntry { + uint32_t physical_address : 20; + uint32_t available : 3; + bool global : 1; + bool pat : 1; + bool dirty : 1; + bool accessed : 1; + bool cache_disabled : 1; + bool write_through : 1; + bool user : 1; + bool write : 1; + bool present : 1; +} __attribute__((packed)); + +PageDirectoryEntry dynamic_page_table[1024] __attribute__((aligned(4096))); // Create a page directory with 1024 entries and make sure it's 4 KiB aligned + +Page::Page() { } + +Page* Page::alloc(void* virtual_addr) { + int i = 0; + while (pages[i].present && i < NUM_PAGES) { i++; } + + pages[i].virtual_addr = virtual_addr; + pages[i].present = true; + + return pages + i; +} + +Page* Page::alloc_fixed(void* physical_address, void* virtual_addr) { + Page* page = Page::from_physical(physical_address); + if (page->exists()) { return 0; } + + page->present = true; + page->virtual_addr = virtual_addr; + + return page; +} + +void Page::free() { + this->unload(); + this->present = false; +} + +bool Page::load() { + return false; +} + +bool Page::unload() { + return false; +} + +bool Page::exists() { + return this->virtual_addr != 0; +} + +void* Page::physical_location() { + return (void*)(0x1000 * (this - pages)); +} + +Page* Page::from_physical(void* physical_address) { + return pages + ((size_t)physical_address >> 12); +} + +size_t Page::pd_entry() { + return (size_t)(this->virtual_addr) >> 22; +} + +size_t Page::pt_entry() { + return ((size_t)(this->virtual_addr) >> 12) & ((1 << 10) - 1); +} + +void Page::initialize() { + // Allocate the lowest 1 MiB of both virtual and physical memory to BIOS + for (size_t i = 0; i < mib(1); i += kib(4)) { + Page::alloc_fixed((void*)i, (void*)i); + } + + /* for (size_t index = _kernel_start; index + kib(4) < _kernel_end; index += kib(4)) { + Page::alloc_fixed(void *physical_address, void *virtual_addr) + } */ +} + diff --git a/src/arch/i686/tty.c b/src/arch/i686/tty.c new file mode 100644 index 0000000..208957e --- /dev/null +++ b/src/arch/i686/tty.c @@ -0,0 +1,55 @@ +#include "arch/tty.h" +#include +#include "strlib.h" + +#define VGA_WIDTH 80 +#define VGA_HEIGHT 25 +#define VGA_MEMORY 0xB8000 + +int x = 0; +int y = 0; +uint16_t color = 0x0F; +uint16_t* tty_buffer = (uint16_t*)VGA_MEMORY; + +void initialize_terminal() { + uint16_t entry = 0; + for (int y = 0; y < VGA_HEIGHT; y++) { + for (int x = 0; x < VGA_WIDTH; x++) { + write_character(x, y, entry); + } + } +} + +uint16_t character_data(char character, uint8_t color) { + return character | (color << 8); +} + +void write_character(int x, int y, uint16_t entry) { + size_t index = VGA_WIDTH * y + x; + tty_buffer[index] = entry; +} + +void put_character(char character) { + if (character == '\n') { + y += 1; + x = 0; + return; + } + + write_character(x, y, character_data(character, color)); + + x += 1; + if (x == VGA_WIDTH) { + x = 0; + y += 1; + if (y == VGA_HEIGHT) { + y = 0; + } + } +} + +void write_string(char* str) { + for (size_t i = 0; i < strlen(str); i++) { + put_character(str[i]); + } +} diff --git a/src/include/arch/paging.hpp b/src/include/arch/paging.hpp new file mode 100644 index 0000000..fd01c82 --- /dev/null +++ b/src/include/arch/paging.hpp @@ -0,0 +1,29 @@ +#include +#include + +class Page { +private: + void* virtual_addr; + bool present; + bool write_enable; + bool user; + + +public: + Page(); + static Page* alloc(void* virtual_addr); + static Page* alloc_fixed(void* physical_address, void* virtual_addr); + bool load(); + bool unload(); + bool exists(); + void free(); + + void* physical_location(); + static Page* from_physical(void* physical_address); + + size_t pd_entry(); + size_t pt_entry(); + + static void initialize(); +}; + diff --git a/src/include/arch/tty.h b/src/include/arch/tty.h new file mode 100644 index 0000000..f9ab231 --- /dev/null +++ b/src/include/arch/tty.h @@ -0,0 +1,27 @@ +#include + +// TODO: Define colors +enum vga_color { + VGA_COLOR_BLACK = 0, + VGA_COLOR_BLUE = 1, + VGA_COLOR_GREEN = 2, + VGA_COLOR_CYAN = 3, + VGA_COLOR_RED = 4, + VGA_COLOR_MAGENTA = 5, + VGA_COLOR_BROWN = 6, + VGA_COLOR_LIGHT_GREY = 7, + VGA_COLOR_DARK_GREY = 8, + VGA_COLOR_LIGHT_BLUE = 9, + VGA_COLOR_LIGHT_GREEN = 10, + VGA_COLOR_LIGHT_CYAN = 11, + VGA_COLOR_LIGHT_RED = 12, + VGA_COLOR_LIGHT_MAGENTA = 13, + VGA_COLOR_LIGHT_BROWN = 14, + VGA_COLOR_WHITE = 15, +}; + +void initialize_terminal(); +uint16_t character_data(char character, uint8_t color); +void write_character(int x, int y, uint16_t entry); +void put_character(char character); +void write_string(char* str); diff --git a/src/include/arch/tty.hpp b/src/include/arch/tty.hpp new file mode 100644 index 0000000..e583495 --- /dev/null +++ b/src/include/arch/tty.hpp @@ -0,0 +1,27 @@ +#include + +// TODO: Define colors +enum vga_color { + VGA_COLOR_BLACK = 0, + VGA_COLOR_BLUE = 1, + VGA_COLOR_GREEN = 2, + VGA_COLOR_CYAN = 3, + VGA_COLOR_RED = 4, + VGA_COLOR_MAGENTA = 5, + VGA_COLOR_BROWN = 6, + VGA_COLOR_LIGHT_GREY = 7, + VGA_COLOR_DARK_GREY = 8, + VGA_COLOR_LIGHT_BLUE = 9, + VGA_COLOR_LIGHT_GREEN = 10, + VGA_COLOR_LIGHT_CYAN = 11, + VGA_COLOR_LIGHT_RED = 12, + VGA_COLOR_LIGHT_MAGENTA = 13, + VGA_COLOR_LIGHT_BROWN = 14, + VGA_COLOR_WHITE = 15, +}; + +extern "C" void initialize_terminal(); +extern "C" uint16_t character_data(char character, uint8_t color); +extern "C" void write_character(int x, int y, uint16_t entry); +extern "C" void put_character(char character); +extern "C" void write_string(char* str); diff --git a/src/include/units.h b/src/include/units.h new file mode 100644 index 0000000..f4537f8 --- /dev/null +++ b/src/include/units.h @@ -0,0 +1,10 @@ +#include + +static inline size_t kib(size_t amounts) { + return 1024 * amounts; +} + +static inline size_t mib(size_t amounts) { + return 1024 * 1024 * amounts; +} + diff --git a/src/kernel/kernel.c b/src/kernel/kernel.c deleted file mode 100644 index bcdf1d4..0000000 --- a/src/kernel/kernel.c +++ /dev/null @@ -1,12 +0,0 @@ -#include -#include -#include "strlib.h" - -uint16_t* VGA_screen = (uint16_t*)0xB8000; -char* text = "Hello World!"; - -void kernel_main() { - for (int i = 0; i < strlen(text); i++) { - VGA_screen[i] = text[i] | 0x0F00; // 0x0F00: two bytes color, two bytes to combine it with text - } -} diff --git a/src/kernel/kernel.cpp b/src/kernel/kernel.cpp new file mode 100644 index 0000000..8d91a45 --- /dev/null +++ b/src/kernel/kernel.cpp @@ -0,0 +1,18 @@ +#include +#include +#include "strlib.h" +#include "arch/paging.hpp" +#include "arch/tty.hpp" + +uint16_t* VGA_screen = (uint16_t*)0xC00B8000; +char* text = "Hello World!"; + +extern "C" void kernel_main() { + initialize_terminal(); + + write_string(text); + + Page::initialize(); + + while (true) { } +} diff --git a/src/kernel/pages.cpp b/src/kernel/pages.cpp new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/src/kernel/pages.cpp @@ -0,0 +1 @@ +