From ad1a74fb8569c40f6e14474eb25fd8e050f854c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20Quei=C3=9Fner?= Date: Sun, 9 Aug 2015 02:42:56 +0200 Subject: [PATCH] initial release. --- .gitignore | 37 ++++++ LICENSE | 22 ++++ Makefile | 29 +++++ README.md | 2 + asm/intr_common_handler.S | 88 +++++++++++++ asm/multiboot.S | 9 ++ asm/start.S | 25 ++++ kernel.ld | 39 ++++++ src/console.c | 222 +++++++++++++++++++++++++++++++++ src/console.h | 74 +++++++++++ src/cpustate.h | 24 ++++ src/init.c | 149 ++++++++++++++++++++++ src/interrupts.c | 253 ++++++++++++++++++++++++++++++++++++++ src/interrupts.h | 39 ++++++ src/intr_stubs.h | 46 +++++++ src/io.h | 13 ++ src/kernel.h | 7 ++ src/malloc.c | 249 +++++++++++++++++++++++++++++++++++++ src/multiboot.h | 91 ++++++++++++++ src/pmm.c | 142 +++++++++++++++++++++ src/pmm.h | 24 ++++ src/stdlib.c | 73 +++++++++++ src/stdlib.h | 81 ++++++++++++ src/varargs.h | 6 + src/vmm.c | 81 ++++++++++++ src/vmm.h | 21 ++++ trainscript.md | 212 ++++++++++++++++++++++++++++++++ 27 files changed, 2058 insertions(+) create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 Makefile create mode 100644 README.md create mode 100644 asm/intr_common_handler.S create mode 100644 asm/multiboot.S create mode 100644 asm/start.S create mode 100644 kernel.ld create mode 100644 src/console.c create mode 100644 src/console.h create mode 100644 src/cpustate.h create mode 100644 src/init.c create mode 100644 src/interrupts.c create mode 100644 src/interrupts.h create mode 100644 src/intr_stubs.h create mode 100644 src/io.h create mode 100644 src/kernel.h create mode 100644 src/malloc.c create mode 100644 src/multiboot.h create mode 100644 src/pmm.c create mode 100644 src/pmm.h create mode 100644 src/stdlib.c create mode 100644 src/stdlib.h create mode 100644 src/varargs.h create mode 100644 src/vmm.c create mode 100644 src/vmm.h create mode 100644 trainscript.md diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6f35d31 --- /dev/null +++ b/.gitignore @@ -0,0 +1,37 @@ +# Gitignore + +obj/* +kernel + +# Object files +*.o +*.ko +*.obj +*.elf + +# Precompiled Headers +*.gch +*.pch + +# Libraries +*.lib +*.a +*.la +*.lo + +# Shared objects (inc. Windows DLLs) +*.dll +*.so +*.so.* +*.dylib + +# Executables +*.exe +*.out +*.app +*.i*86 +*.x86_64 +*.hex + +# Debug files +*.dSYM/ diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..63a1ae6 --- /dev/null +++ b/LICENSE @@ -0,0 +1,22 @@ +The MIT License (MIT) + +Copyright (c) 2015 Felix Queißner + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..9786cda --- /dev/null +++ b/Makefile @@ -0,0 +1,29 @@ +SRCS = $(shell find -name '*.[cS]') +OBJS = $(addsuffix .o,$(basename $(SRCS))) + +CC = gcc +LD = ld + +# define nullptr, but not NULL. +CFLAGS = -m32 -Dnullptr=0 +ASFLAGS = +CCFLAGS = -g -std=c11 -Wall -g -fno-stack-protector -ffreestanding +LDFLAGS = -g -melf_i386 -Tkernel.ld + +kernel: $(OBJS) + $(LD) $(LDFLAGS) -o $@ $(addprefix obj/, $(notdir $^)) + +%.o: %.c + $(CC) $(CFLAGS) $(CCFLAGS) -c -o $(addprefix obj/, $(notdir $@)) $^ + +%.o: %.S + $(CC) $(CFLAGS) $(ASFLAGS) -c -o $(addprefix obj/, $(notdir $@)) $^ + +clean: + rm $(addprefix obj/, $(notdir $(OBJS))) + +run: + qemu-system-i386 -kernel kernel + +.PHONY: clean +.PHONY: run diff --git a/README.md b/README.md new file mode 100644 index 0000000..195d5a1 --- /dev/null +++ b/README.md @@ -0,0 +1,2 @@ +# KernelTemplate +A pretty primitive kernel base with physical and virtual memory management and a console. diff --git a/asm/intr_common_handler.S b/asm/intr_common_handler.S new file mode 100644 index 0000000..1cdc613 --- /dev/null +++ b/asm/intr_common_handler.S @@ -0,0 +1,88 @@ +.macro intr_stub nr +.global intr_stub_\nr +intr_stub_\nr: + pushl $0 + pushl $\nr + jmp intr_common_handler +.endm + +.macro intr_stub_error_code nr +.global intr_stub_\nr +intr_stub_\nr: + pushl $\nr + jmp intr_common_handler +.endm + +// Exceptions +intr_stub 0 +intr_stub 1 +intr_stub 2 +intr_stub 3 +intr_stub 4 +intr_stub 5 +intr_stub 6 +intr_stub 7 +intr_stub_error_code 8 +intr_stub 9 +intr_stub_error_code 10 +intr_stub_error_code 11 +intr_stub_error_code 12 +intr_stub_error_code 13 +intr_stub_error_code 14 +intr_stub 15 +intr_stub 16 +intr_stub_error_code 17 +intr_stub 18 + +// IRQs +intr_stub 32 +intr_stub 33 +intr_stub 34 +intr_stub 35 +intr_stub 36 +intr_stub 37 +intr_stub 38 +intr_stub 39 +intr_stub 40 +intr_stub 41 +intr_stub 42 +intr_stub 43 +intr_stub 44 +intr_stub 45 +intr_stub 46 +intr_stub 47 + +// System call +intr_stub 48 + +intr_stub 80 + +.extern intr_routine +intr_common_handler: + // Save additional cpu state + push %ebp + push %edi + push %esi + push %edx + push %ecx + push %ebx + push %eax + + // Call interrupt handler + push %esp + call intr_routine + add $4, %esp + + // Restore cpu state + pop %eax + pop %ebx + pop %ecx + pop %edx + pop %esi + pop %edi + pop %ebp + + // Fehlercode und Interruptnummer vom Stack nehmen + add $8, %esp + + iret diff --git a/asm/multiboot.S b/asm/multiboot.S new file mode 100644 index 0000000..fc12183 --- /dev/null +++ b/asm/multiboot.S @@ -0,0 +1,9 @@ +.section multiboot +#define MB_MAGIC 0x1badb002 +#define MB_FLAGS 0x0 +#define MB_CHECKSUM -(MB_MAGIC + MB_FLAGS) + +.align 4 +.int MB_MAGIC +.int MB_FLAGS +.int MB_CHECKSUM \ No newline at end of file diff --git a/asm/start.S b/asm/start.S new file mode 100644 index 0000000..9c56137 --- /dev/null +++ b/asm/start.S @@ -0,0 +1,25 @@ +.section .text + +// C-Einstiegspunkt +.extern init + +// .global für Sichtbarkeit im Linker (invers zu static aus C) +.global _start +_start: + // Init stack + mov $kernel_stack, %esp + + // Start Kernel + push %ebx + call init + + // Halte an, no matter what +_stop: + cli + hlt + jmp _stop + + // 16 KB Stackspace +.section .bss +.space 16384 +kernel_stack: diff --git a/kernel.ld b/kernel.ld new file mode 100644 index 0000000..077108e --- /dev/null +++ b/kernel.ld @@ -0,0 +1,39 @@ +/* Bei _start soll die Ausfuehrung losgehen */ +ENTRY(_start) + +/* + * Hier wird festgelegt, in welcher Reihenfolge welche Sektionen in die Binary + * geschrieben werden sollen + */ +SECTIONS +{ + /* + * Startpunkt des Kernels + */ + . = 0x100000; + + /* Kernel Start Punkt */ + kernelStart = .; + + /* + * Two text sections: .text and .multiboot + * Multiboot must be at the start of the kernel + */ + .text : { + *(multiboot) + *(.text) + } + .data ALIGN(4096) : { + *(.data) + } + .rodata ALIGN(4096) : { + *(.rodata) + } + .bss ALIGN(4096) : { + *(.bss) + } + + /* Align the end of the kernel to the page size */ + . = ALIGN(4096); + kernelEnd = .; +} diff --git a/src/console.c b/src/console.c new file mode 100644 index 0000000..bb5cb4e --- /dev/null +++ b/src/console.c @@ -0,0 +1,222 @@ +#include "console.h" +#include "stdlib.h" + +typedef enum +{ + csDefault = 0, + csSetColor, + csSetForeground, + csSetBackground, +} ConsoleState; + +typedef struct +{ + int foreground:4; + int background:4; +} ConsoleColor; + +typedef struct +{ + char c; + uint8_t color; +} ConsoleChar; + +static int cursorX = 0; +static int cursorY = 0; +static ConsoleColor color = { COLOR_LIGHTGRAY, COLOR_BLACK }; +static ConsoleState state = csDefault; + +static ConsoleChar *screen = (ConsoleChar*)0xB8000; + +void ksetpos(int x, int y) +{ + if((x >= 0) && (x < CONSOLE_WIDTH)) + { + cursorX = x; + } + + if((y >= 0) && (y < CONSOLE_HEIGHT)) + { + cursorY = y; + } +} + +void kgetpos(int *x, int *y) +{ + if(x != nullptr) *x = cursorX; + if(y != nullptr) *y = cursorY; +} + + +void ksetcolor(int background, int foreground) +{ + if(background >= 0) + { + background &= 0xF; + color.background = background; + } + if(foreground >= 0) + { + foreground &= 0xF; + color.foreground = foreground; + } +} + +void kgetcolor(int *background, int *foreground) +{ + if(background != nullptr) *background = color.background; + if(foreground != nullptr) *foreground = color.foreground; +} + +void kclear(void) +{ + cursorX = 0; + cursorY = 0; + for(size_t i = 0; i < (CONSOLE_WIDTH * CONSOLE_HEIGHT); i++) + { + screen[i].c = ' '; // Fill screen with spaces + screen[i].color = *((uint8_t*)&color); // Set screen to current color + } +} + +static void newline() +{ + cursorX = 0; + cursorY += 1; + if(cursorY == (CONSOLE_HEIGHT)) + { + // We need to scroll here: + + // Copy everything line on the screen to the previous line + for(int line = 1; line < CONSOLE_HEIGHT; line++) + { + memcpy(&screen[CONSOLE_WIDTH * (line-1)], &screen[CONSOLE_WIDTH * line], CONSOLE_WIDTH * sizeof(ConsoleChar)); + } + // Fill the last line with blanks + for(int i = 0; i < CONSOLE_WIDTH; i++) + { + screen[CONSOLE_WIDTH * (CONSOLE_HEIGHT-1)+i].c = ' '; + screen[CONSOLE_WIDTH * (CONSOLE_HEIGHT-1)+i].color = *((uint8_t*)&color); + } + // Reset y cursor position to previous line (we don't want to write out of bounds) + cursorY -= 1; + } +} + +void kputc(char c) +{ + switch(state) + { + case csSetColor: + { + color = *((ConsoleColor*)&c); + state = csDefault; + return; + } + case csSetForeground: + { + color.foreground = c & 0xF; + state = csDefault; + return; + } + case csSetBackground: + { + color.background = c & 0xF; + state = csDefault; + return; + } + case csDefault: + default: + { + int idx = CONSOLE_WIDTH * cursorY + cursorX; + switch(c) + { + case '\x11': + state = csSetColor; + return; + case '\x12': + state = csSetForeground; + return; + case '\x13': + state = csSetBackground; + return; + case '\r': + // Ignore carriage return + return; + case '\n': + newline(); + return; + default: + screen[idx].color = *((uint8_t*)&color); // Set screen to current color + screen[idx].c = c; + + cursorX += 1; + if(cursorX >= CONSOLE_WIDTH) + { + newline(); + } + return; + } + break; + } + } +} + +void kputs(const char *str) +{ + while(*str) + { + kputc(*str); + str++; + } +} + +void kprintf(const char *format, ...) +{ + char buffer[32]; + va_list vl; + va_start(vl, format); + while(*format != 0) + { + char c = *(format++); + if(c == '%') + { + c = *(format++); + int i; + char *str; + switch(c) + { + case 'd': + case 'i': + i = va_arg(vl, int); + kputs(itoa(i, buffer, 10)); + break; + case 'b': + i = va_arg(vl, int); + kputs(itoa(i, buffer, 2)); + break; + case 'x': + case 'X': + i = va_arg(vl, int); + kputs(itoa(i, buffer, 16)); + break; + case 'c': + c = va_arg(vl, int); + kputc(c); + break; + case 's': + str = va_arg(vl, char*); + kputs(str); + break; + default: + kputc(c); + break; + } + } + else + { + kputc(c); + } + } + va_end(vl); +} \ No newline at end of file diff --git a/src/console.h b/src/console.h new file mode 100644 index 0000000..2496df2 --- /dev/null +++ b/src/console.h @@ -0,0 +1,74 @@ +#pragma once + +#define CONSOLE_WIDTH 80 +#define CONSOLE_HEIGHT 25 + +#define COLOR_BLACK 0x0 +#define COLOR_BLUE 0x1 +#define COLOR_GREEN 0x2 +#define COLOR_CYAN 0x3 +#define COLOR_RED 0x4 +#define COLOR_MAGENTA 0x5 +#define COLOR_BROWN 0x6 +#define COLOR_LIGHTGRAY 0x7 +#define COLOR_GRAY 0x8 +#define COLOR_LIGHTBLUE 0x9 +#define COLOR_LIGHTGREEN 0xA +#define COLOR_LIGHTRED 0xC +#define COLOR_LIGHTMAGENTA 0xD +#define COLOR_YELLOW 0xE +#define COLOR_WHITE 0xF + +/** + * @brief Sets the cursor position. + * @param x Distance of the cursor from the left border. + * @param y Distance of the cursor from the top border. + */ +void ksetpos(int x, int y); + + +/** + * @brief Gets the cursor position. + * @param x Pointer to an integer that should store the distance of the cursor from the left border. + * @param y Pointer to an integer that should store the distance of the cursor from the top border. + */ +void kgetpos(int *x, int *y); + +/** + * @brief Sets the color of the console. + * @param foreground The foreground color. + * @param background The background color. + */ +void ksetcolor(int background, int foreground); + + +/** + * @brief Gets the color of the console. + * @param foreground A pointer to the foreground color. + * @param background A pointer to the background color. + */ +void kgetcolor(int *background, int *foreground); + +/** + * @brief Clears the screen and sets the cursor to the top left corner. + */ +void kclear(void); + +/** + * @brief Puts a character onto the screen. + * @param c The character that should be printed. + */ +void kputc(char c); + +/** + * @brief Prints a string onto the screen. + * @param A null-terminated string that should be printed. + */ +void kputs(const char *str); + +/** + * @brief Prints a formatted string onto the screen. + * @param format The format string that will be printed in formatted version. + * @param ... The format parameters that will be used to print the string. + */ +void kprintf(const char *format, ...); \ No newline at end of file diff --git a/src/cpustate.h b/src/cpustate.h new file mode 100644 index 0000000..20acaa3 --- /dev/null +++ b/src/cpustate.h @@ -0,0 +1,24 @@ +#pragma once +#include "inttypes.h" + +typedef struct +{ + // Saved by interrupt routine + uint32_t eax; + uint32_t ebx; + uint32_t ecx; + uint32_t edx; + uint32_t esi; + uint32_t edi; + uint32_t ebp; + + uint32_t intr; + uint32_t error; + + // Saved by CPU + uint32_t eip; + uint32_t cs; + uint32_t eflags; + uint32_t esp; + uint32_t ss; +} CpuState; \ No newline at end of file diff --git a/src/init.c b/src/init.c new file mode 100644 index 0000000..437d5e6 --- /dev/null +++ b/src/init.c @@ -0,0 +1,149 @@ +#include "kernel.h" +#include "stdlib.h" +#include "console.h" +#include "interrupts.h" +#include "pmm.h" +#include "vmm.h" + +void die(const char *msg) +{ + kputs("\n"); + ksetcolor(COLOR_RED, COLOR_WHITE); + kputs(msg); + while(1) + { + __asm__ volatile ("cli; hlt;"); + } +} + +void ksleep(uint32_t time) +{ + for(uint32_t i = 0; i < time; i++) + { + // BURN, CPU, BURN! + for(volatile size_t i = 0; i < 40000000; i++); + } +} + +static void debug_test() +{ + char buffer[64]; + kputs("itoa test:\n"); + kputs("10 = "); kputs(itoa(10, buffer, 10)); kputc('\n'); + kputs("10 = 0x"); kputs(itoa(10, buffer, 16)); kputc('\n'); + kputs("10 = 0b"); kputs(itoa(10, buffer, 2)); kputc('\n'); + kputs("10 = 0o"); kputs(itoa(10, buffer, 8)); kputc('\n'); + + kputs("printf test:\n"); + kprintf("This %s %c test line.\n", "is", 'a'); + kprintf("Numbers: %d %i %x %b\n", 15, 15, 15, 15); + + /* + kputs("scroll-test:\n"); + for(int i = 0; i < 100; i++) + { + kprintf("They see me scrolling, they hating! %i\n", i); + } + for(int i = 0; i < 272; i++) + { + kprintf("x"); + } + */ +} + +static void dumpMB(const MultibootStructure *mbHeader) +{ + kputs("Multiboot Information:\n"); + if(mbHeader->flags & MB_MEMSIZE) + { + kprintf("Lower Memory: %d kB\n", mbHeader->memLower); + kprintf("Upper Memory: %d kB\n", mbHeader->memUpper); + } + // TODO: MB_BOOTDEVICE + if(mbHeader->flags & MB_COMMANDLINE) + { + kprintf("Commandline: %s\n", mbHeader->commandline); + } + if(mbHeader->flags & MB_MODULES) + { + const MultibootModule *mod = (const MultibootModule *)mbHeader->modules; + for(size_t i = 0; i < mbHeader->moduleCount; i++) + { + kprintf("Module %s [%d - %d]\n", mod[i].name, mod[i].start, mod[i].end); + } + } + if(mbHeader->flags & MB_SYMS_AOUT) + { + kputs("Kernel File Format: a.out\n"); + } + if(mbHeader->flags & MB_SYMS_ELF) + { + kputs("Kernel File Format: ELF\n"); + } + if(mbHeader->flags & MB_MEMORYMAP) + { + uintptr_t it = mbHeader->memoryMap; + kprintf("Memory Map: %d entries\n", mbHeader->memoryMapLength); + for(size_t i = 0; i < mbHeader->memoryMapLength; i++) + { + const MultibootMemoryMap *mmap = (const MultibootMemoryMap *)it; + if(mmap->type == 1) + kprintf("Memory Map: [%d + %d] %s\n", (uint32_t)mmap->base, (uint32_t)mmap->length, mmap->type == 1 ? "free" : "preserved"); + it += mmap->entry_size + 4; // Stupid offset :P + } + } + // TODO: MB_DRIVES + // TODO: MB_CONFIG_TABLE + if(mbHeader->flags & MB_BOOTLOADER_NAME) + { + kprintf("Bootloader Name: %s\n", mbHeader->bootLoaderName); + } + // TODO: MB_APS_TABLE +} + +void init(const MultibootStructure *mbHeader) +{ + (void)debug_test; + (void)dumpMB; + + ksetcolor(COLOR_BLACK, COLOR_LIGHTGRAY); + kclear(); + kputs("Welcome to \x12\x05nucleo\x12\x07!\n"); + + //dumpMB(mbHeader); + + kputs("Initialize physical memory management: "); + pmm_init(mbHeader); + kputs("success.\n"); + // uint32_t freeMem = pmm_calc_free(); + //kprintf("Free memory: %d B, %d kB, %d MB\n", freeMem, freeMem >> 10, freeMem >> 20); + + ksleep(1); + + kputs("Initialize virtual memory management: "); + vmm_init(); + kputs("success.\n"); + + kputs("Initialize interrupts: "); + intr_init(); + kputs("success.\n"); + + kputs("Enable hw interrupts: "); + irq_enable(); + kputs("success.\n"); + + //__asm__ volatile("sti"); + + kputs("Prepare heap memory: "); + for(uintptr_t ptr = 0x400000; ptr < 0x800000; ptr += 4096) + { + vmm_map(ptr, (uintptr_t)pmm_alloc(), VM_PROGRAM); + } + kputs("success.\n"); + + while(1) + { + kputs("x"); + ksleep(1); + } +} diff --git a/src/interrupts.c b/src/interrupts.c new file mode 100644 index 0000000..74c922a --- /dev/null +++ b/src/interrupts.c @@ -0,0 +1,253 @@ +#include "interrupts.h" +#include "console.h" +#include "stdlib.h" +#include "cpustate.h" +#include "io.h" + +#define GDT_ENTRIES 5 +#define IDT_ENTRIES 256 + +static uint64_t gdt[GDT_ENTRIES]; +static uint64_t idt[IDT_ENTRIES]; + +static const char *interruptNames[] = { + "Divide-by-zero Error",// 0 (0x0) Fault #DE No + "Debug",// 1 (0x1) Fault/Trap #DB No + "Non-maskable Interrupt",// 2 (0x2) Interrupt - No + "Breakpoint",// 3 (0x3) Trap #BP No + "Overflow",// 4 (0x4) Trap #OF No + "Bound Range Exceeded",// 5 (0x5) Fault #BR No + "Invalid Opcode",// 6 (0x6) Fault #UD No + "Device Not Available",// 7 (0x7) Fault #NM No + "Double Fault",// 8 (0x8) Abort #DF Yes + "Coprocessor Segment Overrun",// 9 (0x9) Fault - No + "Invalid TSS",// 10 (0xA) Fault #TS Yes + "Segment Not Present",// 11 (0xB) Fault #NP Yes + "Stack-Segment Fault",// 12 (0xC) Fault #SS Yes + "General Protection Fault",// 13 (0xD) Fault #GP Yes + "Page Fault",// 14 (0xE) Fault #PF Yes + "Reserved",// 15 (0xF) - - No + "x87 Floating-Point Exception",// 16 (0x10) Fault #MF No + "Alignment Check",// 17 (0x11) Fault #AC Yes + "Machine Check",// 18 (0x12) Abort #MC No + "SIMD Floating-Point Exception",// 19 (0x13) Fault #XM/#XF No + "Virtualization Exception",// 20 (0x14) Fault #VE No + "Reserved 21",// 21-29 (0x15-0x1D) - - No + "Reserved 22",// 21-29 (0x15-0x1D) - - No + "Reserved 23",// 21-29 (0x15-0x1D) - - No + "Reserved 24",// 21-29 (0x15-0x1D) - - No + "Reserved 25",// 21-29 (0x15-0x1D) - - No + "Reserved 26",// 21-29 (0x15-0x1D) - - No + "Reserved 27",// 21-29 (0x15-0x1D) - - No + "Reserved 28",// 21-29 (0x15-0x1D) - - No + "Reserved 29",// 21-29 (0x15-0x1D) - - No + "Security Exception",// 30 (0x1E) - #SX Yes + "Reserved 31",// 31 (0x1F) - - No + "IRQ 0", + "IRQ 1", + "IRQ 2", + "IRQ 3", + "IRQ 4", + "IRQ 5", + "IRQ 6", + "IRQ 7", + "IRQ 8", + "IRQ 9", + "IRQ 10", + "IRQ 11", + "IRQ 12", + "IRQ 13", + "IRQ 14", + "IRQ 15", +}; + +static const size_t interruptNameCount = sizeof(interruptNames) / sizeof(interruptNames[0]); + +#include "intr_stubs.h" + +void intr_routine(CpuState *state) +{ + const char *name = "Unknown"; + if(state->intr < interruptNameCount) + name = interruptNames[state->intr]; + if(state->intr < 0x20) + { + kprintf("\n\x12\x04Exception [%d] %s!\x12\0x7\n", state->intr, name); + while(1) + { + __asm__ volatile("cli; hlt"); + } + } + if (state->intr >= 0x20 && state->intr <= 0x2f) + { + if (state->intr >= 0x28) + { + // EOI an Slave-PIC + outb(0xa0, 0x20); + } + // EOI an Master-PIC + outb(0x20, 0x20); + } + else + { + kprintf("\n\x12\x04Interrupt [%d] %s occurred!\x12\0x7\n", state->intr, name); + while(1) + { + // Prozessor anhalten + __asm__ volatile("cli; hlt"); + } + } +} + +static void gdt_entry(int i, uint32_t base, uint32_t limit, int flags) +{ + gdt[i] = limit & 0xffffLL; + gdt[i] |= (base & 0xffffffLL) << 16; + gdt[i] |= (flags & 0xffLL) << 40; + gdt[i] |= ((limit >> 16) & 0xfLL) << 48; + gdt[i] |= ((flags >> 8 )& 0xffLL) << 52; + gdt[i] |= ((base >> 24) & 0xffLL) << 56; +} + +static void idt_entry(int i, void (*fn)(), unsigned int selector, int flags) +{ + unsigned long int handler = (unsigned long int) fn; + idt[i] = handler & 0xffffLL; + idt[i] |= (selector & 0xffffLL) << 16; + idt[i] |= (flags & 0xffLL) << 40; + idt[i] |= ((handler>> 16) & 0xffffLL) << 48; +} + +static void init_gdt(void) +{ + memset(gdt, 0, sizeof(gdt)); + + gdt_entry(0, 0, 0, 0); + gdt_entry(1, 0, 0xfffff, GDTF_SEGMENT | GDTF_32_BIT | + GDTF_CODESEG | GDTF_4K_GRAN | GDTF_PRESENT); + gdt_entry(2, 0, 0xfffff, GDTF_SEGMENT | GDTF_32_BIT | + GDTF_DATASEG | GDTF_4K_GRAN | GDTF_PRESENT); + gdt_entry(3, 0, 0xfffff, GDTF_SEGMENT | GDTF_32_BIT | + GDTF_CODESEG | GDTF_4K_GRAN | GDTF_PRESENT | GDTF_RING3); + gdt_entry(4, 0, 0xfffff, GDTF_SEGMENT | GDTF_32_BIT | + GDTF_DATASEG | GDTF_4K_GRAN | GDTF_PRESENT | GDTF_RING3); + + struct + { + uint16_t limit; + void* pointer; + } __attribute__((packed)) gdtp = + { + .limit = GDT_ENTRIES * 8 - 1, + .pointer = gdt, + }; + __asm__ volatile("lgdt %0" : : "m" (gdtp)); + __asm__ volatile( + "mov $0x10, %ax;" + "mov %ax, %ds;" + "mov %ax, %es;" + "mov %ax, %ss;" + "ljmp $0x8, $.1;" + ".1:" + ); +} + +#define IDT_FLAG_INTERRUPT_GATE 0xe +#define IDT_FLAG_PRESENT 0x80 +#define IDT_FLAG_RING0 0x00 +#define IDT_FLAG_RING3 0x60 + +static void init_pic(void) +{ + // Master-PIC initialisieren + outb(0x20, 0x11); // Initialisierungsbefehl fuer den PIC + outb(0x21, 0x20); // Interruptnummer fuer IRQ 0 + outb(0x21, 0x04); // An IRQ 2 haengt der Slave + outb(0x21, 0x01); // ICW 4 + + // Slave-PIC initialisieren + outb(0xa0, 0x11); // Initialisierungsbefehl fuer den PIC + outb(0xa1, 0x28); // Interruptnummer fuer IRQ 8 + outb(0xa1, 0x02); // An IRQ 2 haengt der Slave + outb(0xa1, 0x01); // ICW 4 + + // Alle IRQs aktivieren (demaskieren) + outb(0x20, 0x0); + outb(0xa0, 0x0); +} + +static void init_idt(void) +{ + memset(idt, 0, sizeof(idt)); + +#define SET_ENTRY(i) idt_entry(i, intr_stub_ ## i, 0x08, IDT_FLAG_INTERRUPT_GATE | IDT_FLAG_PRESENT | IDT_FLAG_RING0) + + for(size_t i = 0; i < IDT_ENTRIES; i++) { + idt_entry(i, intr_stub_0, 0x08, IDT_FLAG_INTERRUPT_GATE | IDT_FLAG_PRESENT | IDT_FLAG_RING0); + } + + // System Interrupts + SET_ENTRY(0); + SET_ENTRY(1); + SET_ENTRY(2); + SET_ENTRY(3); + SET_ENTRY(4); + SET_ENTRY(6); + SET_ENTRY(7); + SET_ENTRY(8); + SET_ENTRY(9); + SET_ENTRY(10); + SET_ENTRY(11); + SET_ENTRY(12); + SET_ENTRY(13); + SET_ENTRY(14); + SET_ENTRY(15); + SET_ENTRY(16); + SET_ENTRY(17); + SET_ENTRY(18); + + // Hardware handler + SET_ENTRY(32); + SET_ENTRY(33); + SET_ENTRY(34); + SET_ENTRY(35); + SET_ENTRY(36); + SET_ENTRY(37); + SET_ENTRY(38); + SET_ENTRY(39); + SET_ENTRY(40); + SET_ENTRY(41); + SET_ENTRY(42); + SET_ENTRY(43); + SET_ENTRY(44); + SET_ENTRY(45); + SET_ENTRY(46); + SET_ENTRY(47); + + // System call + SET_ENTRY(48); + +#undef SET_ENTRY + struct + { + uint16_t limit; + void* pointer; + } __attribute__((packed)) idtp = + { + .limit = IDT_ENTRIES * 8 - 1, + .pointer = idt, + }; + __asm__ volatile("lidt %0" : : "m" (idtp)); +} + +void intr_init(void) +{ + // Initialize global descriptor table + init_gdt(); + + // Initialize interrupt descriptor table + init_idt(); + + // Initialize Programmable Interrupt Controller + init_pic(); +} diff --git a/src/interrupts.h b/src/interrupts.h new file mode 100644 index 0000000..4d81174 --- /dev/null +++ b/src/interrupts.h @@ -0,0 +1,39 @@ +#pragma once + +#define GDTF_DATASEG 0x02 +#define GDTF_CODESEG 0x0a +#define GDTF_TSS 0x09 + +#define GDTF_SEGMENT 0x10 +#define GDTF_RING0 0x00 +#define GDTF_RING3 0x60 +#define GDTF_PRESENT 0x80 + +#define GDTF_4K_GRAN 0x800 +#define GDTF_32_BIT 0x400 + +#define INTR_GATE 6 +#define INTR_TRAP_GATE 7 +#define INTR_TASK_GATE 5 + +/** + * Initializes interrupt handling and the global descriptor table. + */ +void intr_init(); + +/** + * @brief Enables physical interrupts. + */ +static inline void irq_enable(void) +{ + __asm__ volatile("sti"); +} + +/** + * @brief Disables physical interrupts. + */ +static inline void irq_disable(void) +{ + __asm__ volatile("cli"); +} + diff --git a/src/intr_stubs.h b/src/intr_stubs.h new file mode 100644 index 0000000..303bbbb --- /dev/null +++ b/src/intr_stubs.h @@ -0,0 +1,46 @@ +#pragma once + +#define STUB(num) extern void intr_stub_ ## num () + +STUB(0); +STUB(1); +STUB(2); +STUB(3); +STUB(4); +STUB(5); +STUB(6); +STUB(7); +STUB(8); +STUB(9); +STUB(10); +STUB(11); +STUB(12); +STUB(13); +STUB(14); +STUB(15); +STUB(16); +STUB(17); +STUB(18); + +STUB(32); +STUB(33); +STUB(34); +STUB(35); +STUB(36); +STUB(37); +STUB(38); +STUB(39); +STUB(40); +STUB(41); +STUB(42); +STUB(43); +STUB(44); +STUB(45); +STUB(46); +STUB(47); + +STUB(48); + +STUB(80); + +#undef STUB diff --git a/src/io.h b/src/io.h new file mode 100644 index 0000000..70e8901 --- /dev/null +++ b/src/io.h @@ -0,0 +1,13 @@ +#pragma once + +#include "inttypes.h" + +/** + * @brief Outputs a byte on the given port. + * @param port The port number. + * @param data The byte to send. + */ +static inline void outb(uint16_t port, uint8_t data) +{ + __asm__ volatile ("outb %0, %1" : : "a" (data), "Nd" (port)); +} diff --git a/src/kernel.h b/src/kernel.h new file mode 100644 index 0000000..9f5dfdd --- /dev/null +++ b/src/kernel.h @@ -0,0 +1,7 @@ +#pragma once + +#include + +void die(const char *msg); + +void ksleep(uint32_t time); diff --git a/src/malloc.c b/src/malloc.c new file mode 100644 index 0000000..cfe75f4 --- /dev/null +++ b/src/malloc.c @@ -0,0 +1,249 @@ +/* Copyright (c) 2002, 2004, 2010 Joerg Wunsch + Copyright (c) 2010 Gerben van den Broeke + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of the copyright holders nor the names of + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ + + +/* $Id: malloc.c 2149 2010-06-09 20:45:37Z joerg_wunsch $ */ + +#include +#include + +struct __freelist { + size_t sz; + struct __freelist *nx; +}; + +size_t __malloc_margin = 32; +char * const __malloc_heap_start = (char *)0x400000; +char * const __malloc_heap_end = (char *)0x800000; + +char *__brkval; +struct __freelist *__flp; + +void *malloc(size_t len) +{ + struct __freelist *fp1, *fp2, *sfp1, *sfp2; + char *cp; + size_t s, avail; + + /* + * Our minimum chunk size is the size of a pointer (plus the + * size of the "sz" field, but we don't need to account for + * this), otherwise we could not possibly fit a freelist entry + * into the chunk later. + */ + if (len < sizeof(struct __freelist) - sizeof(size_t)) + len = sizeof(struct __freelist) - sizeof(size_t); + + /* + * First, walk the free list and try finding a chunk that + * would match exactly. If we found one, we are done. While + * walking, note down the smallest chunk we found that would + * still fit the request -- we need it for step 2. + * + */ + for (s = 0, fp1 = __flp, fp2 = 0; + fp1; + fp2 = fp1, fp1 = fp1->nx) { + if (fp1->sz < len) + continue; + if (fp1->sz == len) { + /* + * Found it. Disconnect the chunk from the + * freelist, and return it. + */ + if (fp2) + fp2->nx = fp1->nx; + else + __flp = fp1->nx; + return &(fp1->nx); + } + else { + if (s == 0 || fp1->sz < s) { + /* this is the smallest chunk found so far */ + s = fp1->sz; + sfp1 = fp1; + sfp2 = fp2; + } + } + } + /* + * Step 2: If we found a chunk on the freelist that would fit + * (but was too large), look it up again and use it, since it + * is our closest match now. Since the freelist entry needs + * to be split into two entries then, watch out that the + * difference between the requested size and the size of the + * chunk found is large enough for another freelist entry; if + * not, just enlarge the request size to what we have found, + * and use the entire chunk. + */ + if (s) { + if (s - len < sizeof(struct __freelist)) { + /* Disconnect it from freelist and return it. */ + if (sfp2) + sfp2->nx = sfp1->nx; + else + __flp = sfp1->nx; + return &(sfp1->nx); + } + /* + * Split them up. Note that we leave the first part + * as the new (smaller) freelist entry, and return the + * upper portion to the caller. This saves us the + * work to fix up the freelist chain; we just need to + * fixup the size of the current entry, and note down + * the size of the new chunk before returning it to + * the caller. + */ + cp = (char *)sfp1; + s -= len; + cp += s; + sfp2 = (struct __freelist *)cp; + sfp2->sz = len; + sfp1->sz = s - sizeof(size_t); + return &(sfp2->nx); + } + /* + * Step 3: If the request could not be satisfied from a + * freelist entry, just prepare a new chunk. This means we + * need to obtain more memory first. The largest address just + * not allocated so far is remembered in the brkval variable. + * Under Unix, the "break value" was the end of the data + * segment as dynamically requested from the operating system. + * Since we don't have an operating system, just make sure + * that we don't collide with the stack. + */ + if (__brkval == 0) + __brkval = __malloc_heap_start; + cp = __malloc_heap_end; + if (cp <= __brkval) + /* + * Memory exhausted. + */ + return 0; + avail = cp - __brkval; + /* + * Both tests below are needed to catch the case len >= 0xfffe. + */ + if (avail >= len && avail >= len + sizeof(size_t)) { + fp1 = (struct __freelist *)__brkval; + __brkval += len + sizeof(size_t); + fp1->sz = len; + return &(fp1->nx); + } + /* + * Step 4: There's no help, just fail. :-/ + */ + return 0; +} + + +void free(void *p) +{ + struct __freelist *fp1, *fp2, *fpnew; + char *cp1, *cp2, *cpnew; + + /* ISO C says free(NULL) must be a no-op */ + if (p == 0) + return; + + cpnew = p; + cpnew -= sizeof(size_t); + fpnew = (struct __freelist *)cpnew; + fpnew->nx = 0; + + /* + * Trivial case first: if there's no freelist yet, our entry + * will be the only one on it. If this is the last entry, we + * can reduce __brkval instead. + */ + if (__flp == 0) { + if ((char *)p + fpnew->sz == __brkval) + __brkval = cpnew; + else + __flp = fpnew; + return; + } + + /* + * Now, find the position where our new entry belongs onto the + * freelist. Try to aggregate the chunk with adjacent chunks + * if possible. + */ + for (fp1 = __flp, fp2 = 0; + fp1; + fp2 = fp1, fp1 = fp1->nx) { + if (fp1 < fpnew) + continue; + cp1 = (char *)fp1; + fpnew->nx = fp1; + if ((char *)&(fpnew->nx) + fpnew->sz == cp1) { + /* upper chunk adjacent, assimilate it */ + fpnew->sz += fp1->sz + sizeof(size_t); + fpnew->nx = fp1->nx; + } + if (fp2 == 0) { + /* new head of freelist */ + __flp = fpnew; + return; + } + break; + } + /* + * Note that we get here either if we hit the "break" above, + * or if we fell off the end of the loop. The latter means + * we've got a new topmost chunk. Either way, try aggregating + * with the lower chunk if possible. + */ + fp2->nx = fpnew; + cp2 = (char *)&(fp2->nx); + if (cp2 + fp2->sz == cpnew) { + /* lower junk adjacent, merge */ + fp2->sz += fpnew->sz + sizeof(size_t); + fp2->nx = fpnew->nx; + } + /* + * If there's a new topmost chunk, lower __brkval instead. + */ + for (fp1 = __flp, fp2 = 0; + fp1->nx != 0; + fp2 = fp1, fp1 = fp1->nx) + /* advance to entry just before end of list */; + cp2 = (char *)&(fp1->nx); + if (cp2 + fp1->sz == __brkval) { + if (fp2 == NULL) + /* Freelist is empty now. */ + __flp = NULL; + else + fp2->nx = NULL; + __brkval = cp2 - sizeof(size_t); + } +} diff --git a/src/multiboot.h b/src/multiboot.h new file mode 100644 index 0000000..425396d --- /dev/null +++ b/src/multiboot.h @@ -0,0 +1,91 @@ +#pragma once + +#include + +#define MB_MEMSIZE (1<<0) +#define MB_BOOTDEVICE (1<<1) +#define MB_COMMANDLINE (1<<2) +#define MB_MODULES (1<<3) +#define MB_SYMS_AOUT (1<<4) +#define MB_SYMS_ELF (1<<5) +#define MB_MEMORYMAP (1<<6) +#define MB_DRIVES (1<<7) +#define MB_CONFIG_TABLE (1<<8) +#define MB_BOOTLOADER_NAME (1<<9) +#define MB_APS_TABLE (1<<10) +#define MB_VBE (1<<11) + +typedef struct { + uint32_t entry_size; + uint64_t base; + uint64_t length; + uint32_t type; +} __attribute__((packed)) MultibootMemoryMap; + +typedef struct { + uintptr_t start; + uintptr_t end; + uintptr_t name; + uint32_t reserved; +} __attribute__((packed)) MultibootModule; + +typedef struct { + uint32_t size; + uint8_t number; + uint8_t mode; + uint16_t cylinders; + uint8_t heads; + uint8_t sectors; + uint16_t ports[0]; + // 0x10 size-0x10 drive_ports I/O-Ports, die von diesem Gerät benutzt werden +} __attribute__((packed)) MultibootDrive; + +typedef struct +{ + uint16_t version; + uint16_t cseg; + uint32_t offset; + uint16_t cseg_16; + uint16_t dseg; + uint16_t flags; + uint16_t cseg_len; + uint16_t cseg_16_len; + uint16_t dseg_len; +} __attribute__((packed)) MultibootAPMTable; + +typedef struct { + uint32_t flags; + uint32_t memLower; + uint32_t memUpper; + uint32_t bootDevice; + uintptr_t commandline; + uint32_t moduleCount; + uintptr_t modules; + union { + struct { + uint32_t tabsize; + uint32_t strsize; + uint32_t addr; + uint32_t reserved; + } __attribute__((packed)) symsAssemblerOut; + struct { + uint32_t num; + uint32_t size; + uintptr_t addr; + uintptr_t shndx; + } __attribute__((packed)) symsELF; + }; + uint32_t memoryMapLength; + uintptr_t memoryMap; + uint32_t drivesLength; + uintptr_t drives; + uintptr_t configTable; + uintptr_t bootLoaderName; + uintptr_t apmTable; + uint32_t vbeControlInfo; + uint32_t vbeModeInfo; + uint16_t vbeMode; + uint16_t vbeInterfaceSegment; + uint16_t vbeInterfaceOffset; + uint16_t vbeInterfaceLength; +} __attribute__((packed)) MultibootStructure; diff --git a/src/pmm.c b/src/pmm.c new file mode 100644 index 0000000..0f334f9 --- /dev/null +++ b/src/pmm.c @@ -0,0 +1,142 @@ +#include +#include "pmm.h" +#include "kernel.h" +#include "stdlib.h" + +#include "console.h" + +#define BITMAP_SIZE 32768 + +extern const void kernelStart; +extern const void kernelEnd; + +/** + * Stores for every page in 4 GB RAM if it is free or not. + * A set bit indicates that the page is free. + */ +static uint32_t bitmap[BITMAP_SIZE]; + +static inline void resolve(void *pptr, uint32_t *idx, uint32_t *bit) +{ + uintptr_t ptr = (uintptr_t)pptr; + ptr >>= 12; // Calculate page ID + + *idx = ptr / 32; // B32 offset in bitmap. + *bit = ptr % 32; // Bit offset in bitmap +} + +static inline void *rebuild(uint32_t idx, uint32_t bit) +{ + uintptr_t ptr = 32 * idx + bit; + ptr <<= 12; + return (void*)ptr; +} + +/** + * Marks an address used or free + * @param addr The address that will be marked + * @param used If != 0, the addr will be marked as used, else it will be marked as free. + */ +static inline void mark(uintptr_t addr, uint32_t used) +{ + uint32_t bit, idx; + resolve((void*)addr, &idx, &bit); + if(used) + bitmap[idx] &= ~(1<flags & MB_MEMORYMAP) == 0) + die("Multiboot header missing memory map. Cannot initialize PMM."); + + // Make all memory used + memset(bitmap, 0, sizeof(bitmap)); + + // Free the memory map + { + uintptr_t it = mb->memoryMap; + for(size_t i = 0; i < mb->memoryMapLength; i++) + { + const MultibootMemoryMap *mmap = (const MultibootMemoryMap *)it; + if(mmap->type == 1) + { + markSection(mmap->base, mmap->base + mmap->length, 0); + } + it += mmap->entry_size + 4; // Stupid offset :P + } + } + + // Mark the whole kernel as "used" + { + uintptr_t start = (uintptr_t)&kernelStart; + uintptr_t end = (uintptr_t)&kernelEnd; + markSection(start, end, 1); + } + + // Mark all kernel modules as "used" + if(mb->flags & MB_MODULES) + { + const MultibootModule *mod = (const MultibootModule *)mb->modules; + for(size_t i = 0; i < mb->moduleCount; i++) + { + markSection(mod[i].start, mod[i].end, 1); + } + } +} + +uint32_t pmm_calc_free(void) +{ + uint32_t mem = 0; + for(uint32_t idx = 0; idx < BITMAP_SIZE; idx++) + { + for(uint32_t bit = 0; bit < 32; bit++) + { + if(bitmap[idx] & (1< 9)? (rem-10) + 'A' : rem + '0'; + num = num/base; + } + + // If number is negative, append '-' + if (isNegative) + { + str[i++] = '-'; + } + str[i] = '\0'; // Append string terminator + + // Reverse the string + reverse(str, i); + + return str; +} + +int atoi(const char *str) +{ + int res = 0; + for (int i = 0; str[i] != '\0'; ++i) + { + res = res * 10 + str[i] - '0'; + } + return res; +} + +void *memmove( void *destination, const void *source, size_t num) +{ + // TODO: Implement memmove + return nullptr; +} diff --git a/src/stdlib.h b/src/stdlib.h new file mode 100644 index 0000000..b85d2fa --- /dev/null +++ b/src/stdlib.h @@ -0,0 +1,81 @@ +#pragma once + +#include +#include +#include "varargs.h" + +char *itoa(int value, char *str, int base); +int atoi(const char *str); +float atof(const char *str); + +/** + * Allocates a block of memory + * @param size Minimum size of the memory block + * @return Pointer to the allocated memory area + */ +void *malloc(size_t size); + +/** + * Frees a previously allocated block of memory. + * @param mem The block of memory. + */ +void free(void *mem); + +static inline void *memset(void *ptr, int value, size_t num) +{ + uint8_t *it = (uint8_t*)ptr; + while((num--) > 0) + { + *(it++) = (uint8_t)(value & 0xFF); + } + return ptr; +} + +static inline void *memcpy(void *destination, const void *source, size_t num) +{ + uint8_t *to = (uint8_t*)destination; + uint8_t *from = (uint8_t*)source; + while((num--) > 0) + { + *(to++) = *(from++); + } + return destination; +} + +void *memmove(void *destination, const void *source, size_t num); + +static inline char *strcpy(char *destination, const char *source) +{ + while(*source) + { + *(destination++) = *(source++); + } + return destination; +} + +static inline char *strcat(char *destination, const char *source) +{ + char *it = destination; + while(*it++); + it--; + while(*source) + { + *it++ = *source++; + } + *it = 0; + return destination; +} + +static inline size_t strlen(const char *str) +{ + size_t size = 0; + while(*(str++) != 0) size++; + return size; +} + +static inline void *calloc(size_t size) +{ + void *mem = malloc(size); + memset(mem, 0, size); + return mem; +} diff --git a/src/varargs.h b/src/varargs.h new file mode 100644 index 0000000..9aac995 --- /dev/null +++ b/src/varargs.h @@ -0,0 +1,6 @@ +#pragma once + +typedef __builtin_va_list va_list; +#define va_start(ap, X) __builtin_va_start(ap, X) +#define va_arg(ap, type) __builtin_va_arg(ap, type) +#define va_end(ap) __builtin_va_end(ap) \ No newline at end of file diff --git a/src/vmm.c b/src/vmm.c new file mode 100644 index 0000000..443a3c7 --- /dev/null +++ b/src/vmm.c @@ -0,0 +1,81 @@ +#include "vmm.h" +#include "pmm.h" +#include "stdlib.h" +#include "console.h" +#include "kernel.h" + +typedef struct +{ + uintptr_t *directory; +} VmmContext; + +VmmContext *context = nullptr; + +VmmContext* vmm_create_context(void) +{ + VmmContext* context = (VmmContext*)pmm_alloc(); + + // Initialize page directory + context->directory = pmm_alloc(); + for (uint32_t i = 0; i < 1024; i++) + { + context->directory[i] = 0; + } + return context; +} + +static inline void vmm_activate_context(VmmContext* context) +{ + __asm__ volatile("mov %0, %%cr3" : : "r" (context->directory)); +} + +void vmm_map(uintptr_t virtual, uintptr_t physical, uint32_t flags) +{ + // Prepare parameters + // Align 12 bit + virtual = (virtual >> 12) << 12; + physical = (physical >> 12) << 12; + flags &= 0x0007; + + uint32_t pageIndex = virtual / 4096; + + uint32_t dirIdx = pageIndex / 1024; // Index in page directory + uint32_t tableIdx = pageIndex % 1024; // Index in page table + + uintptr_t *table = nullptr; + if((context->directory[dirIdx] & VM_PRESENT) == 0) + { + // Allocate page table if not exists. + table = pmm_alloc(); + for(uint32_t i = 0; i < 1024; i++) + { + table[i] = 0; + } + context->directory[dirIdx] = (uintptr_t)table | VM_PRESENT | VM_WRITABLE; + } + else + { + table = (uintptr_t*)(context->directory[dirIdx] & 0xfffff000); + } + table[tableIdx] = physical | flags; + + __asm__ volatile("invlpg %0" : : "m" (*(char*)virtual)); +} + +void vmm_init(void) +{ + context = vmm_create_context(); + + /* Die ersten 4 MB an dieselbe physische wie virtuelle Adresse mappen */ + for (uintptr_t ptr = 0; ptr < 0x400000; ptr += 0x1000) + { + vmm_map(ptr, ptr, VM_PRESENT | VM_WRITABLE); + } + + vmm_activate_context(context); + + uint32_t cr0; + __asm__ volatile("mov %%cr0, %0" : "=r" (cr0)); + cr0 |= (1 << 31); + __asm__ volatile("mov %0, %%cr0" : : "r" (cr0)); +} diff --git a/src/vmm.h b/src/vmm.h new file mode 100644 index 0000000..4190cbf --- /dev/null +++ b/src/vmm.h @@ -0,0 +1,21 @@ +#pragma once + +#include + +#define VM_PRESENT 0x01 +#define VM_WRITABLE 0x02 +#define VM_USER 0x04 + +#define VM_KERNEL (VM_PRESENT | VM_WRITABLE +#define VM_PROGRAM (VM_PRESENT | VM_WRITABLE | VM_USER) + +void vmm_init(void); + +/** + * Maps a physical address to a virtual address. + * @param virtual The virtual address to be mapped. + * @param physical Target physical address + * @param flags Flags for the mapping + * @remarks If flags is 0, the mapping will be undone. + */ +void vmm_map(uintptr_t virtual, uintptr_t physical, uint32_t flags); diff --git a/trainscript.md b/trainscript.md new file mode 100644 index 0000000..cd0f724 --- /dev/null +++ b/trainscript.md @@ -0,0 +1,212 @@ +# Trainscript Version 1 Specification + +## Basic Language Features + +The language is not case sensitive. + +Keywords should be written **CAPITALIZED**. + +Identifieres should be written **lowercased**. + +### Comments +Comments are written with a starting # + + normal code + # comment + normal code # comment + +### Types +The language has the following built in primitive types: + +- VOID +- BOOL +- INT +- REAL +- TEXT + +`VOID` is a special type that can only be used in function +return values indicating the function does not return anything + +### Pointers +The language supports pointers by using special operators. +Pointers can exist to every type the language supports, not +including pointers. + +### Blocks +Blocks are defined by indentation in this language. An +indentation by a 2 spaces is required: + + blockA + blockB + blockB + blockC + blockC + blockb + blockA + blockA + +## Grammarless Description + +### Variable Declaration + + VAR name : type; + +### Expressions And Assignments + +An expression is a piece of code that calculates a value. +An assignment assigns the result of an expression to a +variable. The assignment is either done by `->` or `→`. + + 1 → a; + a → b; + a + b → c; + a * (b + c) → d; + +### Control Structures + +The language supports the most basic control structures: + +- IF +- IF/ELSE +- IF/ELSEIF/ELSE +- REPEAT +- REPEAT WHILE +- REPEAT UNTIL +- REPEAT FROM TO + +Each control structure except `REPEAT` takes an expression +for defining the control. The expression is executed once or +multiple times depending on the behaviour of the control +structure. + + # Simple condition + IF expr + loc; + + # Condition with alternative + IF expr + loc; + ELSE + loc; + + # Condition with multiple alternatives + IF expr + loc; + ELSEIF expr2 + loc; + + IF expr + loc; + ELSEIF expr2 + loc; + ELSE + loc; + + # Endless loop + REPEAT + loc; + + # "While" loop + REPEAT WHILE expr + loc; + + # Inverted "Do-While" loop + REPEAT UNTIL expr + loc; + + # "For" loop + REPEAT a FROM 1 TO 10 + loc; + +### Function Calls + +Every function returns a value and thus can be included in an expression. +Functions calls do not have to be in an expression and can discard the return value. + + fn(); + fn(a); + fn(a,b); + fn() → a; + fn(a) → b; + fn(a,b) → c; + +### Function Declaration + +Functions are declared by using either the `PUB` or the `PRI` keyword. +Each script file can be seen as a module that exports a set of functions. +`PUB` declares a function that can be seen from outside the module, `PRI` +declares a hidden function. + +Each function has a set of local variables that have a specific type. + +Returning a value is done by declaring a special local variable that can +be named as wanted. + + # Program that returns 0 + PUB main() → result : INT + 0 → result; + + PUB noReturn() + otherFn(); + + # Private function that doubles the given value squared. + PRI fn(x : REAL) → y : REAL + 2 * x * x → y; + + +### Pointer Declaration And Usage + +A pointer is declared by using `PTR(type)` as the type. + + VAR name : PTR(INT); + +To retrieve a pointer value or write to it, `VAL(pointer)` must be used: + + VAL(pointer) → a; + a → VAL(pointer); + +The address of a variable can be retrieved by using `REF(variable)`: + + REF(variable) → pointer; + +## Example File + + PUB inOut() | a : INT + printStr("Enter a number:"); + readInt() → a; + printStr("You entered: "); + printInt(a); + printLn(); # Prints a newline ('\n') + + PUB isEven(x : INT) → even : BOOL + IF (x % 2) =/= 0 + false → even + ELSE + true → even + + PUB fibonacci(n : INT) → f : INT + IF n = 0 + 0 → f; + ELSEIF n = 1 + 1 → f; + ELSE + fibonacci(n - 1) + fibonacci(n - 2) → f; + + PUB factorial(number : INT) → result : INT + IF number <= 1 + 1 → result; + ELSE + number * factorial(number - 1) → result; + + PRI double(ptr : PTR(INT)) → previous : INT + VAL(ptr) → previous; + 2 * previous → VAL(ptr); + + PUB ptrTest() | a : INT, b : INT + 10 → a; + double(REF(a)) → b; + IF a = 20 + printStr("double has doubled a."); + IF a = (2 * b) + printStr("double has returned the correct previous value"); + \ No newline at end of file