initial release.

This commit is contained in:
Felix Queißner 2015-08-09 02:42:56 +02:00
commit ad1a74fb85
27 changed files with 2058 additions and 0 deletions

37
.gitignore vendored Normal file
View file

@ -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/

22
LICENSE Normal file
View file

@ -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.

29
Makefile Normal file
View file

@ -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

2
README.md Normal file
View file

@ -0,0 +1,2 @@
# KernelTemplate
A pretty primitive kernel base with physical and virtual memory management and a console.

88
asm/intr_common_handler.S Normal file
View file

@ -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

9
asm/multiboot.S Normal file
View file

@ -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

25
asm/start.S Normal file
View file

@ -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:

39
kernel.ld Normal file
View file

@ -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 = .;
}

222
src/console.c Normal file
View file

@ -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);
}

74
src/console.h Normal file
View file

@ -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, ...);

24
src/cpustate.h Normal file
View file

@ -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;

149
src/init.c Normal file
View file

@ -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);
}
}

253
src/interrupts.c Normal file
View file

@ -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();
}

39
src/interrupts.h Normal file
View file

@ -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");
}

46
src/intr_stubs.h Normal file
View file

@ -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

13
src/io.h Normal file
View file

@ -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));
}

7
src/kernel.h Normal file
View file

@ -0,0 +1,7 @@
#pragma once
#include <inttypes.h>
void die(const char *msg);
void ksleep(uint32_t time);

249
src/malloc.c Normal file
View file

@ -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 <stdlib.h>
#include <stdint.h>
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);
}
}

91
src/multiboot.h Normal file
View file

@ -0,0 +1,91 @@
#pragma once
#include <inttypes.h>
#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;

142
src/pmm.c Normal file
View file

@ -0,0 +1,142 @@
#include <inttypes.h>
#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<<bit);
else
bitmap[idx] |= (1<<bit);
}
/**
* Marks a section in memory as used or free.
* @param start The start address of the section.
* @param end The end address of the section.
* @param used If 0 the section will be freed, else it will be marked as used.
*/
static inline void markSection(uintptr_t start, uintptr_t end, uint32_t used)
{
uintptr_t it = start;
while(it < end)
{
mark(it, used);
it += 0x1000;
}
}
void pmm_init(const MultibootStructure *mb)
{
if((mb->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<<bit))
mem += 0x1000;
}
}
return mem;
}
void pmm_free(void *pptr)
{
mark((uintptr_t)pptr, 0);
}
void *pmm_alloc(void)
{
for(uint32_t idx = 0; idx < BITMAP_SIZE; idx++)
{
for(uint32_t bit = 0; bit < 32; bit++)
{
if(bitmap[idx] & (1<<bit))
{
// Allocate here
bitmap[idx] &= ~(1<<bit);
return rebuild(idx, bit);
}
}
}
die("out of physical memory");
return (void*)0xDEADBEEF;
}

24
src/pmm.h Normal file
View file

@ -0,0 +1,24 @@
#pragma once
#include "multiboot.h"
/**
* Initializes physical memory management.
* @param mb The multi boot structure used for gathering information about free memory.
*/
void pmm_init(const MultibootStructure *mb);
/**
* Frees a page of physical memory.
*/
void pmm_free(void *pptr);
/**
* Allocates a page of physical memory.
*/
void *pmm_alloc(void);
/**
* Calculates the free memory in bytes.
*/
uint32_t pmm_calc_free(void);

73
src/stdlib.c Normal file
View file

@ -0,0 +1,73 @@
#include "stdlib.h"
static void reverse(char *str, int length)
{
int start = 0;
int end = length -1;
while (start < end)
{
char tmp = *(str+start);
*(str+start) = *(str+end);
*(str+end) = tmp;
start++;
end--;
}
}
char *itoa(int num, char *str, int base)
{
int i = 0;
int isNegative = 0;
/* Handle 0 explicitely, otherwise empty string is printed for 0 */
if (num == 0)
{
str[i++] = '0';
str[i] = '\0';
return str;
}
// In standard itoa(), negative numbers are handled only with
// base 10. Otherwise numbers are considered unsigned.
if (num < 0 && base == 10)
{
isNegative = 1;
num = -num;
}
// Process individual digits
while (num != 0)
{
int rem = num % base;
str[i++] = (rem > 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;
}

81
src/stdlib.h Normal file
View file

@ -0,0 +1,81 @@
#pragma once
#include <stddef.h>
#include <inttypes.h>
#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;
}

6
src/varargs.h Normal file
View file

@ -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)

81
src/vmm.c Normal file
View file

@ -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));
}

21
src/vmm.h Normal file
View file

@ -0,0 +1,21 @@
#pragma once
#include <inttypes.h>
#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);

212
trainscript.md Normal file
View file

@ -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");