aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorNic Gaffney <gaffney_nic@protonmail.com>2023-11-05 06:13:22 -0600
committerNic Gaffney <gaffney_nic@protonmail.com>2023-11-05 06:13:22 -0600
commit036398ab051f2df5b70303e03f4f6506d36cb6eb (patch)
tree0067db99aa1417bb58b3b4d65b960a47b1100475 /src
downloadgftos-036398ab051f2df5b70303e03f4f6506d36cb6eb.tar.gz
Initial Commit
Diffstat (limited to 'src')
-rw-r--r--src/header/boot.s117
-rw-r--r--src/include/print.h31
-rw-r--r--src/include/str.h5
-rw-r--r--src/kernel/compile_flags.txt1
-rw-r--r--src/kernel/kernel.c21
-rw-r--r--src/kernel/print.c133
-rw-r--r--src/linker.ld44
-rw-r--r--src/utils/str.c33
8 files changed, 385 insertions, 0 deletions
diff --git a/src/header/boot.s b/src/header/boot.s
new file mode 100644
index 0000000..2276e3d
--- /dev/null
+++ b/src/header/boot.s
@@ -0,0 +1,117 @@
+/* multiboot header constants */
+.set ALIGN, 1<<0 /* Align loaded modules on page boundries */
+.set MEMINFO, 1<<1 /* Provide memory map*/
+.set FLAGS, ALIGN | MEMINFO /* Multiboot flag field*/
+.set MAGIC, 0x1BADB002 /*Lets bootloader find header*/
+.set CHECKSUM, -(MAGIC + FLAGS) /* Proves we are multiboot via checksum*/
+
+/*
+ * Declare a multiboot header that marks the program as a kernel.
+ * These are magic values that are docymented in the multiboot standard.
+ * The bootloader will search fot this signature in the first 8KiB of
+ * the kernel file, aligned at a 32-bit boundry.
+ * The signature is in its own section so the header can be forced to be
+ * within the first 8KiB of the kernel file.
+ */
+.section .multiboot
+.align 4
+.long MAGIC
+.long FLAGS
+.long CHECKSUM
+
+/*
+ * The multiboot standard doesn't define the value of the "stack pointer register,"
+ * or esp, as it is up to the kernel to provide a stack.
+ * This allocates room for a small stack through a few steps:
+ * 1. Create a symbol at the bottom of the stack
+ * 2. Allocate 16 KiB for the stack
+ * 3. Create a symbol at the top of the stack.
+ * The stack grows DOWN on x86.
+ * Since the stack is in its own section*, it can be marked "nobits"
+ * which means the kernel file is smaller since it doesn't contain
+ * an uninitialized stack.
+ * On x86, the stack must be 16-byte aligned** according to System V ABI standard
+ * and de-facto extentions.
+ * The compiler assumes the stack is properly aligned, so failure to align will
+ * result in UB.
+ */
+.section bss
+.align 16
+.stack_bottom:
+.skip 16384
+stack_top:
+
+/*
+ * Our linker script specifies _start as te entry point to the kernel.
+ * The bootloader will jump here once the kernel is loaded.
+ * We won't return, since the bootloader would be gone at that point
+ */
+.section .text
+.global _start
+.type _start, @function
+_start:
+ /*
+ * The bootloader loads us into 32-bit mode on x86 machines.
+ * Interrupts and paging are disabled.
+ * The multiboot standard defines our current processor state.
+ * The kernel has full control over the CPU
+ * The kernel can only use two things
+ * 1. hardware features
+ * 2. its own code
+ * No printing is available (Unless we make it).
+ * No security restrictions, safeguards, or debugging mechanisms.
+ * We only have what the kernel provides.
+ * ABSOLUTE POWER OVER THE MACHINE!!
+ */
+
+ /*
+ * To setup a stack, make the esp register point to the top of the stack.
+ * We have to do this in assembly since C wont even function without the stack.
+ */
+ mov $stack_top, %esp
+
+ /*
+ * This is a good point to initialize crucial processor state before our
+ * high level kernel is entered.
+ * It's best to minize early environment where crucial features are offline.
+ * The processor is not fully initialized yet, therefore
+ * features such as floating point instructions and instruction set extentions
+ * are ALSO not initialized.
+ * The GDT (global descriptor table) should be loaded here.
+ * Paging should be enabled here.
+ */
+
+ /*
+ * Enter our high level kernel.
+ * the ABI (?) requres the stack is 16-byte aligned at the time of
+ * the call instruction (which afterward will push the return pointer
+ * of size 4 bytes).
+ * The stack was 16-byte aligned above, and we have pushed a multiple of
+ * 16 bytes to the stack (we have pushed zero bytes so far), so
+ * the alignment is preserved, and the call is well defined.
+ */
+ call kernel_main
+
+ /*
+ * If the system has nothing else to do, we will infinitely loop.
+ * To do this, we must:
+ * 1. Disable interrupts with `cli`
+ * (clear interrupt enable in eflags.)
+ * They are already disabled by the bootloader, so we can skip this.
+ * Keep in mind, we may have to do this later AND return from main
+ * (for some nonsensical reason)
+ * 2. wait for the next interrupt to arrive with `hlt` (halt instruction)
+ * Since interrupts are disabled, this locks up the computer (yay!)
+ * 3. jump to the `hlt` instruction if it ever wakes up due to a
+ * non-maskable interrupt occuring or due to a system management mode.
+ */
+ cli
+1: hlt
+ jmp 1
+
+/*
+ * Set the size of _start to { current_location - (start of _start) }
+ * This is useful for debugging or for call tracing later on.
+ */
+.size _start, . - _start
+
diff --git a/src/include/print.h b/src/include/print.h
new file mode 100644
index 0000000..a8e228b
--- /dev/null
+++ b/src/include/print.h
@@ -0,0 +1,31 @@
+#pragma once
+
+#include <stddef.h>
+#include <stdint.h>
+
+// Define colors for printing
+enum {
+ PRINT_COLOR_BLACK = 0,
+ PRINT_COLOR_BLUE = 1,
+ PRINT_COLOR_GREEN = 2,
+ PRINT_COLOR_CYAN = 3,
+ PRINT_COLOR_RED = 4,
+ PRINT_COLOR_MAGENTA = 5,
+ PRINT_COLOR_BROWN = 6,
+ PRINT_COLOR_LIGHT_GRAY = 7,
+ PRINT_COLOR_DARK_GRAY = 8,
+ PRINT_COLOR_LIGHT_BLUE = 9,
+ PRINT_COLOR_LIGHT_GREEN = 10,
+ PRINT_COLOR_LIGHT_CYAN = 11,
+ PRINT_COLOR_LIGHT_RED = 12,
+ PRINT_COLOR_PINK = 13,
+ PRINT_COLOR_YELLOW = 14,
+ PRINT_COLOR_WHITE = 15,
+};
+
+// Prototypes
+void print_clear();
+void print_char(char);
+void print_str(char *);
+void print_set_color(uint8_t, uint8_t);
+void printf(const char *, ...);
diff --git a/src/include/str.h b/src/include/str.h
new file mode 100644
index 0000000..11b8408
--- /dev/null
+++ b/src/include/str.h
@@ -0,0 +1,5 @@
+#pragma once
+#include <stddef.h>
+char *itoa(int, char *);
+size_t strlen(const char *);
+char *str_reverse(char *);
diff --git a/src/kernel/compile_flags.txt b/src/kernel/compile_flags.txt
new file mode 100644
index 0000000..11ef7f9
--- /dev/null
+++ b/src/kernel/compile_flags.txt
@@ -0,0 +1 @@
+-I../include/
diff --git a/src/kernel/kernel.c b/src/kernel/kernel.c
new file mode 100644
index 0000000..50da3c6
--- /dev/null
+++ b/src/kernel/kernel.c
@@ -0,0 +1,21 @@
+#include "print.h"
+
+// Check if the compiler thinks we are targeting the wrong OS
+#if defined(__linux__)
+#error "Not using cross compiler!"
+#endif
+
+// Only works for 32 bit ix86 target
+#if !defined(__i386__)
+#error "Must use ix86-elf compiler"
+#endif
+
+// Color Constants
+
+void kernel_main(void) {
+ print_clear();
+ print_set_color(PRINT_COLOR_YELLOW, PRINT_COLOR_BLACK);
+ printf("printf\n\tint: %d\n\tstring: %s\n\tchar: %c\n\tpercent: "
+ "%%\n\tnothing: %n",
+ 1287, "World! ", 't');
+}
diff --git a/src/kernel/print.c b/src/kernel/print.c
new file mode 100644
index 0000000..35533cd
--- /dev/null
+++ b/src/kernel/print.c
@@ -0,0 +1,133 @@
+#include "print.h"
+#include "str.h"
+#include <stdarg.h>
+// Constants
+static const size_t NUM_COLS = 80;
+static const size_t NUM_ROWS = 25;
+
+// Char struct
+struct Char {
+ uint8_t character;
+ uint8_t color;
+};
+
+// Set video mem buffer
+struct Char *buffer = (struct Char *)0xb8000;
+// Set current row and col
+size_t col = 0;
+size_t row = 0;
+
+// Default colors
+uint8_t color = PRINT_COLOR_WHITE | PRINT_COLOR_BLACK << 4;
+
+// clear row
+void clear_row(size_t row) {
+ // Define empty character
+ struct Char empty = (struct Char){
+ character : ' ',
+ color : color,
+ };
+
+ // For every col in row, set vmem to empty
+ for (size_t col = 0; col < NUM_COLS; col++) {
+ buffer[col + NUM_COLS * row] = empty;
+ }
+}
+
+// Call clear_row for every row
+void print_clear() {
+ for (size_t i = 0; i < NUM_ROWS; i++) {
+ clear_row(i);
+ }
+}
+
+// newline
+void print_newline() {
+ // Reset col and iterate row if not at bottom of screen
+ col = 0;
+ if (row < NUM_ROWS - 1) {
+ row++;
+ return;
+ }
+
+ // If at bottom of screen, move all the text up one row
+ for (size_t row = 1; row < NUM_ROWS; row++) {
+ for (size_t col = 0; col < NUM_COLS; col++) {
+ struct Char character = buffer[col + NUM_COLS * row];
+ buffer[col + NUM_COLS * (row - 1)] = character;
+ }
+ }
+
+ // Clear
+ clear_row(NUM_ROWS - 1);
+}
+
+// printchar
+void print_char(char character) {
+ // If \n, call newline
+ if (character == '\n') {
+ print_newline();
+ return;
+ }
+
+ if (character == '\t') {
+ print_str(" \0");
+ return;
+ }
+
+ // if cols is too large, overflow
+ if (col > NUM_COLS) {
+ print_newline();
+ }
+
+ // set video memeory to requested character
+ buffer[col + NUM_COLS * row] = (struct Char){
+ character : (uint8_t)character,
+ color : color,
+ };
+
+ // iterate col
+ col++;
+}
+
+// print_str
+void print_str(char *str) {
+ // while character != '\0' print char
+ for (size_t i = 0; str[i] != '\0'; ++i)
+ print_char(str[i]);
+}
+
+// Set color byte to requested colors using color enum from print.h
+void print_set_color(uint8_t foreground, uint8_t background) {
+ color = foreground + (background << 4);
+}
+
+void printf(const char *str, ...) {
+ va_list args;
+ va_start(args, str);
+ char temp_str[256];
+
+ for (size_t i = 0; str[i] != '\0'; ++i) {
+ if (str[i] == '%') {
+ switch (str[++i]) {
+ case 'i':
+ case 'd':
+ print_str(itoa(va_arg(args, int), temp_str));
+ break;
+ case 's':
+ print_str(va_arg(args, char *));
+ break;
+ case 'c':
+ print_char(va_arg(args, int));
+ break;
+ case '%':
+ print_char('%');
+ case 'n':
+ break;
+ }
+ continue;
+ }
+ print_char(str[i]);
+ }
+ va_end(args);
+}
diff --git a/src/linker.ld b/src/linker.ld
new file mode 100644
index 0000000..8a9d5f8
--- /dev/null
+++ b/src/linker.ld
@@ -0,0 +1,44 @@
+/* The bootloader witll look at this image and start execution at
+ * the symbol desugbated as ENTRY
+ *
+ * In this case, it is _start in boot.s
+ */
+ENTRY (_start)
+
+/* Tell where the sections of the object files (.o) will be put in the
+ * final image
+ */
+SECTIONS
+{
+ /* Put sections at 1MiB, the conventional place for kernels to be
+ * loaded at by the bootloader. */
+ . = 1M;
+
+ /* First, put the multiboot header. It is required to be put
+ * VERY early in the image or the bootloader wont recognize the file
+ * format. Next, we will put the .text section*/
+ .text BLOCK(4K) : ALIGN(4K)
+ {
+ *(.multiboot)
+ *(.text)
+ }
+ /* Read only data */
+ .rodata BLOCK(4K) : ALIGN(4K)
+ {
+ *(.rodata)
+ }
+ /* Read write data (initialized) */
+ .data BLOCK(4K) : ALIGN(4K)
+ {
+ *(.data)
+ }
+ /* Read write data (uninitialized) and stack */
+ .bss BLOCK(4K) : ALIGN(4K)
+ {
+ *(COMMON)
+ *(.bss)
+ }
+
+ /* The compiler may produce other sections, but they will be put in
+ * a section with the same name by default. Add stuff here as needed */
+}
diff --git a/src/utils/str.c b/src/utils/str.c
new file mode 100644
index 0000000..499f781
--- /dev/null
+++ b/src/utils/str.c
@@ -0,0 +1,33 @@
+#include "str.h"
+#include <stddef.h>
+
+size_t strlen(const char *str) {
+ size_t size = 0;
+ for (; (str[size]) != '\0'; size++) {
+ }
+ return size;
+}
+
+char *str_reverse(char *str) {
+ char *str_ptr = str;
+ size_t size = strlen(str);
+ char return_str[size];
+
+ for (size_t i = 0; i < size; i++)
+ return_str[size - i - 1] = str[i];
+
+ for (size_t i = 0; i < size; i++)
+ *str_ptr++ = return_str[i];
+
+ return str;
+}
+
+char *itoa(int num, char *str) {
+ char *str_ptr = str;
+ for (; num;) {
+ *str_ptr++ = num % 10 + 48;
+ num /= 10;
+ }
+ str_reverse(str);
+ return str;
+}