From a3c817412e5a2fb5e792c581097b625b8200a382 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=A1s=20Ortega=20Froysa?= Date: Thu, 8 Mar 2018 16:53:29 +0100 Subject: [PATCH] Initial commit. --- .gitignore | 7 +++ Makefile | 44 +++++++++++++ configs/grub.cfg | 3 + src/kernel/arch/x86/boot.s | 112 ++++++++++++++++++++++++++++++++++ src/kernel/arch/x86/linker.ld | 33 ++++++++++ src/kernel/arch/x86/tty.c | 84 +++++++++++++++++++++++++ src/kernel/arch/x86/vga.h | 31 ++++++++++ src/kernel/kernel.c | 6 ++ src/kernel/ports.h | 21 +++++++ src/kernel/system.h | 3 + src/kernel/tty.h | 8 +++ 11 files changed, 352 insertions(+) create mode 100644 .gitignore create mode 100644 Makefile create mode 100644 configs/grub.cfg create mode 100644 src/kernel/arch/x86/boot.s create mode 100644 src/kernel/arch/x86/linker.ld create mode 100644 src/kernel/arch/x86/tty.c create mode 100644 src/kernel/arch/x86/vga.h create mode 100644 src/kernel/kernel.c create mode 100644 src/kernel/ports.h create mode 100644 src/kernel/system.h create mode 100644 src/kernel/tty.h diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c755f56 --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +# ignore binary files +*.bin +*.o +*.iso + +# ignore build files +isodir diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..abed973 --- /dev/null +++ b/Makefile @@ -0,0 +1,44 @@ +# General options +TARGET=i686 +ARCH=$(shell echo $(TARGET) | sed s/i.86/x86/) +# Assembly options +AS=i686-elf-as +AFLAGS= +# C options +CC=i686-elf-gcc +CFLAGS?=-O0 -g +CFLAGS:=$(CFLAGS) -std=gnu99 -ffreestanding -Wall -Wextra -Isrc/ +# Linker options +LDFLAGS?=-O0 +LDFLAGS:=$(LDFLAGS) -ffreestanding -nostdlib +LIBS=-lgcc + +# Binary variables +OBJS=src/kernel/arch/$(ARCH)/boot.o src/kernel/kernel.o src/kernel/arch/$(ARCH)/tty.o + +untrue.bin: $(OBJS) + $(CC) -T src/kernel/arch/$(ARCH)/linker.ld -o $@ $(LDFLAGS) $^ $(LIBS) + +%.o: %.c + $(CC) $(CFLAGS) -c $< -o $@ + +%.o: %.s + $(AS) $(AFLAGS) $< -o $@ + +.PHONY: all build-iso clean clean-all +all: untrue.bin + +build-iso: untrue.iso + +clean: + rm -f $(OBJS) + rm -rf isodir + +clean-all: clean + rm -f *.iso *.bin + +untrue.iso: untrue.bin + mkdir -p isodir/boot/grub/ + cp configs/grub.cfg isodir/boot/grub/ + cp $< isodir/boot/ + grub-mkrescue -o $@ isodir diff --git a/configs/grub.cfg b/configs/grub.cfg new file mode 100644 index 0000000..4c6b135 --- /dev/null +++ b/configs/grub.cfg @@ -0,0 +1,3 @@ +menuentry "UntrueOS" { + multiboot /boot/untrue.bin +} diff --git a/src/kernel/arch/x86/boot.s b/src/kernel/arch/x86/boot.s new file mode 100644 index 0000000..ad264a0 --- /dev/null +++ b/src/kernel/arch/x86/boot.s @@ -0,0 +1,112 @@ +# declare constants for the multiboot header +.set ALIGN, 1 << 0 # align loaded modules on page boundaries +.set MEMINFO, 1 << 1 # provide memory map +.set FLAGS, ALIGN | MEMINFO # the multiboot `FLAG' field +.set MAGIC, 0x1BADB002 # 'magic number' letting the boot loader know we're here +.set CHECKSUM , -(MAGIC + FLAGS) # checksum of the above to prove we're multiboot + +/* + * Declare the multiboot header marking this program as a kernel. The bootloader + * will search for these values in the first 8 KiB of the kernel file aligned at + * 32-bit boundaries (4 bytes). We put the signature in its own section to force + * it within the first 8 KiB of the kernel file. + */ +.section .multiboot +.align 4 +.long MAGIC +.long FLAGS +.long CHECKSUM + +/* + * Create a 16 byte aligned stack with 16 KiB of size. We create labels at the + * bottom and top of the stack. + */ +.section .bss +.align 16 +stack_bottom: +.skip 16384 # 16 KiB +stack_top: + +/* + * Create the GDT + */ +.section .data +gdt_start: +gdt_null: +.long 0x0 +.long 0x0 + +gdt_kcode: +.word 0xFFFF # limit +.word 0x0 # base +.byte 0x0 # base +.byte 0b10011010 # 1st flags | type flags +.byte 0b11001111 # 2nd flags | limit +.byte 0x0 # base + +gdt_kdata: +.word 0xFFFF # limit +.word 0x0 # base +.byte 0x0 # base +.byte 0b10010010 # 1st flags | type flags +.byte 0b11001111 # 2nd flags | limit +.byte 0x0 # base + +gdt_ucode: +.word 0xFFFF # limit +.word 0x0 # base +.byte 0x0 # base +.byte 0b11111010 # 1st flags | type flags +.byte 0b11001111 # 2nd flags | limit +.byte 0x0 # base + +gdt_udata: +.word 0xFFFF # limit +.word 0x0 # base +.byte 0x0 # base +.byte 0b11110010 # 1st flags | type flags +.byte 0b11001111 # 2nd flags | limit +.byte 0x0 # base + +gdt_end: + +gdtr: +.word (gdt_end - gdt_start - 1) +.long gdt_start + +/* + * The linker script specifies the `_start' label as the entry point to the kernel + * and the bootloader will jump to this position. That is, this is where the kernel + * starts. + */ +.section .text +.global _start +.type _start, @function +_start: + # set the position of `%esp' to the top of the stack + mov $stack_top, %esp + + # GDT, paging, and other features +flush_gdt: + cli + lgdt (gdtr) + movw $0x10, %ax + movw %ax, %ds + movw %ax, %es + movw %ax, %fs + movw %ax, %gs + movw %ax, %ss + ljmp $0x08, $flush_end +flush_end: + +boot_kernel: + # enter high-level kernel (C) + call kernel_main + + # put computer into infinite loop + cli # disable interrupts by clearing the interrupt flag in `eflags' +end_loop: + hlt # wait for next interrupt + jmp end_loop # jump to the `hlt' instruction if it ever leaves it + +.size _start, . - _start diff --git a/src/kernel/arch/x86/linker.ld b/src/kernel/arch/x86/linker.ld new file mode 100644 index 0000000..d0d8bbd --- /dev/null +++ b/src/kernel/arch/x86/linker.ld @@ -0,0 +1,33 @@ +ENTRY(_start) + +SECTIONS +{ + /* put sections at 1 MiB as is conventional for kernels */ + . = 1M; + + /* place multiboot header early for boot loader and then `.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) + } +} diff --git a/src/kernel/arch/x86/tty.c b/src/kernel/arch/x86/tty.c new file mode 100644 index 0000000..c5cb229 --- /dev/null +++ b/src/kernel/arch/x86/tty.c @@ -0,0 +1,84 @@ +#include +#include + +#include + +#include "vga.h" + +#define VGA_WIDTH 80 +#define VGA_HEIGHT 25 +#define VGA_MEMORY 0xb8000 + + +static size_t tty_row; +static size_t tty_col; +static uint8_t tty_color; +static uint16_t *tty_buffer; + +void tty_init() { + tty_row = 0; + tty_col = 0; + tty_color = vga_entry_color(VGA_COLOR_LIGHT_GREY, VGA_COLOR_BLACK); + tty_buffer = (uint16_t*) VGA_MEMORY; + for(size_t y = 0; y < VGA_HEIGHT; ++y) + { + for(size_t x = 0; x < VGA_WIDTH; ++x) + { + const size_t index = GET_INDEX(x, y); + tty_buffer[index] = vga_entry(' ', tty_color); + } + } +} + +void tty_setcolor(uint8_t color) { + tty_color = color; +} + +void tty_putentryat(unsigned char c, uint8_t color, size_t x, size_t y) { + const size_t index = GET_INDEX(x, y); + tty_buffer[index] = vga_entry(c, color); +} + +void tty_scroll_down() { + // clear first line + for(size_t x = 0; x < VGA_WIDTH; ++x) + { + const size_t i = GET_INDEX(x, 0); + tty_buffer[i] = vga_entry(' ', tty_color); + } + + // scroll text + for(size_t y = 1; y < VGA_HEIGHT; ++y) + { + for(size_t x = 0; x < VGA_WIDTH; ++x) + { + const size_t srci = GET_INDEX(x, y); + const size_t dsti = GET_INDEX(x, y - 1); + tty_buffer[dsti] = tty_buffer[srci]; + } + } +} + +void tty_putchar(unsigned char c) { + tty_putentryat(c, tty_color, tty_col, tty_row); + if(++tty_col == VGA_WIDTH) + { + tty_col = 0; + if(tty_row + 1 == VGA_HEIGHT) + tty_scroll_down(); + else + tty_row++; + } +} + +void tty_write(const char *data, size_t size) { + for(size_t i = 0; i < size; ++i) + tty_putchar(data[i]); +} + +void tty_write_s(const char *str) { + size_t len = 0; + while(str[len]) + len++; + tty_write(str, len); +} diff --git a/src/kernel/arch/x86/vga.h b/src/kernel/arch/x86/vga.h new file mode 100644 index 0000000..347a396 --- /dev/null +++ b/src/kernel/arch/x86/vga.h @@ -0,0 +1,31 @@ +#pragma once + +#include +#define GET_INDEX(x, y) y * VGA_WIDTH + x + +enum vga_color { + VGA_COLOR_BLACK = 0x0, + VGA_COLOR_BLUE = 0x1, + VGA_COLOR_GREEN = 0x2, + VGA_COLOR_CYAN = 0x3, + VGA_COLOR_RED = 0x4, + VGA_COLOR_MAGENTA = 0x5, + VGA_COLOR_BROWN = 0x6, + VGA_COLOR_LIGHT_GREY = 0x7, + VGA_COLOR_DARK_GREY = 0x8, + VGA_COLOR_LIGHT_BLUE = 0x9, + VGA_COLOR_LIGHT_GREEN = 0xA, + VGA_COLOR_LIGHT_CYAN = 0xB, + VGA_COLOR_LIGHT_RED = 0xC, + VGA_COLOR_LIGHT_MAGENTA = 0xD, + VGA_COLOR_LIGHT_BROWN = 0xE, + VGA_COLOR_WHITE = 0xF, +}; + +static inline uint8_t vga_entry_color(enum vga_color fg, enum vga_color bg) { + return fg | bg << 4; +} + +static inline uint16_t vga_entry(unsigned char c, uint8_t color) { + return (uint16_t) c | (uint16_t) color << 8; +} diff --git a/src/kernel/kernel.c b/src/kernel/kernel.c new file mode 100644 index 0000000..1e9bf5b --- /dev/null +++ b/src/kernel/kernel.c @@ -0,0 +1,6 @@ +#include + +void kernel_main() { + tty_init(); + tty_write_s("Hello from the kernel!"); +} diff --git a/src/kernel/ports.h b/src/kernel/ports.h new file mode 100644 index 0000000..455c6c1 --- /dev/null +++ b/src/kernel/ports.h @@ -0,0 +1,21 @@ +#pragma once + +static inline void outb(uint16_t port, uint8_t data) { + __asm__("outb %%al, %%dx" : : "a" (data), "d" (port)); +} + +static inline void outw(uint16_t port, uint16_t data) { + __asm__("outw %%ax, %%dx" : : "a" (data), "d" (port)); +} + +static inline uint8_t inb(uint16_t port) { + uint8_t res; + __asm__("inb %%dx, %%al" : "=a" (res) : "d" (port)); + return res; +} + +static inline uint16_t inw(uint16_t port) { + uint16_t res; + __asm__("inw %%dx, %%ax" : "=a" (res) : "d" (port)); + return res; +} diff --git a/src/kernel/system.h b/src/kernel/system.h new file mode 100644 index 0000000..037982b --- /dev/null +++ b/src/kernel/system.h @@ -0,0 +1,3 @@ +static inline void abort() { + while(1); +} diff --git a/src/kernel/tty.h b/src/kernel/tty.h new file mode 100644 index 0000000..9c475c9 --- /dev/null +++ b/src/kernel/tty.h @@ -0,0 +1,8 @@ +#pragma once + +#include + +void tty_init(); +void tty_putchar(unsigned char c); +void tty_write(const char *data, size_t size); +void tty_write_s(const char *str);