aboutsummaryrefslogtreecommitdiff
path: root/kernel
diff options
context:
space:
mode:
Diffstat (limited to 'kernel')
-rw-r--r--kernel/boot.s93
-rw-r--r--kernel/kernel.cc75
-rw-r--r--kernel/makefile2
-rw-r--r--kernel/vga.cc61
-rw-r--r--kernel/vga.h39
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;
+};