diff options
Diffstat (limited to 'kernel')
-rw-r--r-- | kernel/boot.s | 93 | ||||
-rw-r--r-- | kernel/kernel.cc | 75 | ||||
-rw-r--r-- | kernel/makefile | 2 | ||||
-rw-r--r-- | kernel/vga.cc | 61 | ||||
-rw-r--r-- | kernel/vga.h | 39 |
5 files changed, 270 insertions, 0 deletions
diff --git a/kernel/boot.s b/kernel/boot.s new file mode 100644 index 0000000..bb446d8 --- /dev/null +++ b/kernel/boot.s @@ -0,0 +1,93 @@ +/* + Declare a multiboot header that marks the program as a kernel. + https://www.gnu.org/software/grub/manual/multiboot2/html_node/index.html + + The Multiboot2 header must be contained completely within the first + 32kB of the OS image, and must be 64-bit (8 byte) aligned. +*/ +.set MULTIBOOT_HEADER_MAGIC, 0xe85250d6 # multiboot2 magic number +.set MULTIBOOT_ARCHITECTURE, 0 # protected mode i386 +.set MULTIBOOT_HEADER_TAG_END, 0 + +.section .multiboot +.align 8 +header_start: + .int MULTIBOOT_HEADER_MAGIC + .int MULTIBOOT_ARCHITECTURE + .int header_end - header_start + .int -(MULTIBOOT_HEADER_MAGIC + MULTIBOOT_ARCHITECTURE + (header_end - header_start)) + + # TODO tags go here + + .short MULTIBOOT_HEADER_TAG_END + .short 0 + .int 8 +header_end: + +/* + The stack on x86 must be 16-byte aligned according to the System V ABI + standard and de-facto extensions. The compiler will assume the stack is + properly aligned and failure to align the stack will result in undefined + behavior. +*/ +.section .stack, "aw", @nobits +.align 16 +stack_bottom: + .skip 16 * 1024 +stack_top: + +/* + The linker script specifies _start as the entry point to the kernel and the + bootloader will jump to this position once the kernel has been loaded. +*/ +.section .text +.global _start +.type _start, @function +_start: + mov $stack_top, %esp # point the stack pointer to the stack + + /* + This is a good place to initialize crucial processor state before the + high-level kernel is entered. It's best to minimize the early + environment where crucial features are offline. Note that the + processor is not fully initialized yet: Features such as floating + point instructions and instruction set extensions are not initialized + yet. The GDT should be loaded here. Paging should be enabled here. + C++ features such as global constructors and exceptions will require + runtime support to work as well. + */ + + pushl %ebx # push the pointer to the multiboot structure + pushl %eax # push the multiboot magic value + + /* + Enter the high-level kernel. The ABI requires the stack is 16-byte + aligned at the time of the call instruction (which afterwards pushes + the return pointer of size 4 bytes). The stack was originally 16-byte + aligned above and we've pushed a multiple of 16 bytes to the + stack since (pushed 0 bytes so far), so the alignment has thus been + preserved and the call is well defined. + */ + call kernel_main + + /* + If the system has nothing more to do, put the computer into an + infinite loop. To do that: + 1) Disable interrupts with cli (clear interrupt enable in eflags). + They are already disabled by the bootloader, so this is not needed. + Mind that you might later enable interrupts and return from + kernel_main (which is sort of nonsensical to do). + 2) Wait for the next interrupt to arrive with hlt (halt instruction). + Since they are disabled, this will lock up the computer. + 3) Jump to the hlt instruction if it ever wakes up due to a + non-maskable interrupt occurring or due to system management mode. + */ + cli +hang: hlt + jmp hang + +/* +Set the size of the _start symbol to the current location '.' minus its start. +This is useful when debugging or when you implement call tracing. +*/ +.size _start, . - _start diff --git a/kernel/kernel.cc b/kernel/kernel.cc new file mode 100644 index 0000000..9ea5c95 --- /dev/null +++ b/kernel/kernel.cc @@ -0,0 +1,75 @@ +/* Check if the compiler thinks you are targeting the wrong operating system. */ +#if defined(__linux__) +#error "You are not using a cross-compiler" +#endif + +/* This tutorial will only work for the 32-bit ix86 targets. */ +#if !defined(__i386__) +#error "This tutorial needs to be compiled with a ix86-elf compiler" +#endif + +#include <multiboot2.h> +#include <stdlib.h> +#include <types.h> +#include "vga.h" + +extern "C" void kernel_main(uint32_t mb_magic, uint32_t mb_addr) { + VGA terminal; + Console::set(&terminal); + + printk("Hello, kernel World!\n"); + + printk("multiboot magic: ", uhex{mb_magic}, mb_magic == MULTIBOOT2_BOOTLOADER_MAGIC ? " valid" : " invalid", '\n'); + printk("multiboot addr: ", uhex{mb_addr}, !(mb_addr & 7) ? " is aligned" : " is not aligned", '\n'); + + struct multiboot_tag* tag; + const uint32_t size = *(unsigned*)mb_addr; + printk("Announced mbi size ", size, '\n'); + + for (tag = (struct multiboot_tag*)(mb_addr + 8); tag->type != MULTIBOOT_TAG_TYPE_END; + tag = (struct multiboot_tag*)((multiboot_uint8_t*)tag + ((tag->size + 7) & ~7))) { + switch (tag->type) { + case MULTIBOOT_TAG_TYPE_CMDLINE: { + auto* t = reinterpret_cast<multiboot_tag_string*>(tag); + printk("Command line = [", t->string, "]\n"); + } break; + + case MULTIBOOT_TAG_TYPE_BOOT_LOADER_NAME: { + auto* t = reinterpret_cast<multiboot_tag_string*>(tag); + printk("Boot loader name = [", t->string, "]\n"); + } break; + + case MULTIBOOT_TAG_TYPE_BOOTDEV: { + auto* t = reinterpret_cast<multiboot_tag_bootdev*>(tag); + printk("Boot device ", uhex{t->biosdev}, " slice ", t->slice, " part ", t->part, '\n'); + } break; + + case MULTIBOOT_TAG_TYPE_BASIC_MEMINFO: { + auto* t = reinterpret_cast<multiboot_tag_basic_meminfo*>(tag); + printk("mem_lower = ", t->mem_lower, "KB\n", "mem_upper = ", t->mem_upper, "KB\n"); + } break; + + case MULTIBOOT_TAG_TYPE_MMAP: { + auto* t = reinterpret_cast<multiboot_tag_mmap*>(tag); + multiboot_memory_map_t* mmap; + + printk("memory map\n"); + for (mmap = t->entries; (multiboot_uint8_t*)mmap < (multiboot_uint8_t*)tag + tag->size; + mmap = (multiboot_memory_map_t*)((unsigned long)mmap + ((struct multiboot_tag_mmap*)tag)->entry_size)) { + printk(" base_addr = ", uhex{(unsigned)(mmap->addr >> 32)}, ' ', uhex{(unsigned)(mmap->addr & 0xffffffff)}); + printk(" length = ", (unsigned)(mmap->len >> 32), ' ', (unsigned)(mmap->len & 0xffffffff)); + printk(" type = ", (unsigned)mmap->type, '\n'); + } + } break; + + default: + printk("Tag ", tag->type, ", Size ", tag->size, '\n'); + break; + } // switch(tag->type) + } // for(each tag) + + tag = (struct multiboot_tag*)((multiboot_uint8_t*)tag + ((tag->size + 7) & ~7)); + printk("Total mbi size ", (unsigned)tag - mb_addr, '\n'); + + abort(); +} diff --git a/kernel/makefile b/kernel/makefile new file mode 100644 index 0000000..23e3683 --- /dev/null +++ b/kernel/makefile @@ -0,0 +1,2 @@ +AS_OBJ += kernel/boot.o +CXX_OBJ += kernel/kernel.o kernel/vga.o diff --git a/kernel/vga.cc b/kernel/vga.cc new file mode 100644 index 0000000..83d0060 --- /dev/null +++ b/kernel/vga.cc @@ -0,0 +1,61 @@ +#include "vga.h" +#include <string.h> + +constexpr uint8_t vga_entry_color(VGA::vga_color fg, VGA::vga_color bg) { + return fg | bg << 4; +} + +constexpr uint16_t vga_entry(unsigned char uc, uint8_t color) { + return (uint16_t)uc | (uint16_t)color << 8; +} + +VGA::VGA(vga_color fg, vga_color bg, uint32_t address) { + color = vga_entry_color(fg, bg); + buffer = (uint16_t*)address; + + // clear buffer + for (size_t y = 0; y < max_rows; y++) { + for (size_t x = 0; x < max_columns; x++) { + const size_t index = y * max_columns + x; + buffer[index] = vga_entry(' ', color); + } + } +} + +void VGA::write(char c) { + switch (c) { + case '\n': + column = 0; + ++row; + break; + default: { + const size_t index = row * max_columns + column; + buffer[index] = vga_entry(c, (color == 0) ? this->color : color); + ++column; + } + } + + if (column == max_columns) { + column = 0; + ++row; + } + + if (row == max_rows) { + // scroll up - move rows 1~25 up by one + for (size_t y = 1; y < max_rows; y++) { + const auto prev_y = y - 1; + for (size_t x = 0; x < max_columns; ++x) { + const auto prev = prev_y * max_columns + x; + const auto idx = y * max_columns + x; + buffer[prev] = buffer[idx]; + } + } + --row; + } +} + +void VGA::write(ViewIterator& iter) { + while (iter) { + write(iter.next()); + } +} diff --git a/kernel/vga.h b/kernel/vga.h new file mode 100644 index 0000000..3052dbc --- /dev/null +++ b/kernel/vga.h @@ -0,0 +1,39 @@ +#pragma once +#include <stdlib.h> + +class VGA : public Console { +public: + /* Hardware text mode color constants. */ + 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, + }; + + VGA(vga_color fg = VGA_COLOR_BLACK, vga_color bg = VGA_COLOR_LIGHT_GREY, uint32_t address = 0xB8000); + ~VGA() = default; + + void write(char c) override; + void write(ViewIterator& iter) override; + + void set_color(vga_color fg, vga_color bg) { color = (fg | bg << 4); } + +private: + const size_t max_columns = 80, max_rows = 25; + size_t column = 0, row = 0; + uint8_t color; + uint16_t* buffer; +}; |