@@ -65,14 +65,14 @@ /*!
* Pushes the contents of the general purpose registers onto the stack. */ __attribute__((always_inline)) inline void pusha(void) { - __asm__ volatile ("pusha"); + __asm__ volatile ("pusha" : : : "eax", "ebx", "ecx", "edx", "esi", "edi"); } /*! * Pops the contents of the general purpose registers off the top of the stack. */ __attribute__((always_inline)) inline void popa(void) { - __asm__ volatile ("popa"); + __asm__ volatile ("popa" : : : "eax", "ebx", "ecx", "edx", "esi", "edi"); } /*!@@ -86,6 +86,41 @@ "mov %%ecx, %1;"
"mov %%edx, %2" : "=m" (*ebx), "=m" (*ecx), "=m" (*edx) : "Nd" (eax)); +} + +/*! + * Used to find out the current value of the flags register. + * \return Value of the EFLAGS-register. + */ +__attribute((always_inline)) inline uint32_t eflags() { + uint32_t eflags; + __asm__ volatile ("pushf; pop %%eax; mov %%eax, %0" : "=m" (eflags) : : "eax"); + return eflags; +} + +/*! + * Waits for the next interrupt. + */ +__attribute__((always_inline)) inline void hlt(void) { + __asm__ volatile ("hlt"); +} + +/*! + * Disables interrupts and returns the current EFLAGS-value. + * \return Current value of the EFLAGS-register. + */ +__attribute__((always_inline)) inline uint32_t disable_interrupts(void) { + uint32_t eflags; + __asm__ volatile ("pushf; cli; pop %%eax; mov %%eax, %0" : "=m" (eflags) : : "eax"); + return eflags; +} + +/*! + * Restores a prior state of the EFLAGS-register. Used in tandem with \m disable_interrupts. + * \param eflags Prior state of the EFLAGS-register. + */ +__attribute__((always_inline)) inline void restore_interrupts(uint32_t eflags) { + __asm__ volatile ("mov %0, %%eax; push %%eax; popf" : : "m" (eflags) : "eax"); } /*!
@@ -27,7 +27,7 @@ * Sets cr3 to new page directory and returns the old one.
* \param page_dir Address of new page directory that is supposed to be used. * \return Address of old page directory. */ -__attribute__((always_inline)) inline void* switch_page_dir(void* page_dir) { +__attribute__((always_inline)) inline PHYS_ADDR switch_page_dir(PHYS_ADDR page_dir) { uint32_t res; __asm__ volatile ( "mov %%cr3, %0\n" "mov %1, %%cr3" :@@ -44,6 +44,15 @@ __asm__ volatile ( "mov %cr3, %eax\n"
"mov %eax, %cr3"); } +/*! + * Maps the specified range of physical addresses to the specified virtual address. + * \param page_dir Page directory on which to map the addresses. + * \param dest Virtual address to which the physical address \p src is to be mapped to. + * \param src Physical address which is to be mapped to virtual address \p dest. + * \param page_count Number of pages to map. + * \param flags Access flags for the memory range. + * \return 1 on success, 0 on failure + */ int map_range_to(PHYS_ADDR page_dir, VIRT_ADDR dest, PHYS_ADDR src, uint32_t page_count, uint32_t flags); /*!@@ -51,5 +60,11 @@ * Uses a free page to generate a new page directory which maps its last entry to itself.
* \return Address of new page directory. */ PHYS_ADDR create_empty_page_dir(void); + +/*! + * Initializes paging. + * \return 1 on success, 0 on failure + */ +int paging_init(void); #endif
@@ -6,35 +6,76 @@ #define PROCESS_H
#include "cedos/mm/paging.h" +/*! + * Defines all possible states for processes. + */ typedef enum { + PSTATE_TERMINATED PSTATE_READY, PSTATE_RUNNING, PSTATE_BLOCKED, - PSTATE_TERMINATED } PROCESS_STATE; +/*! + * Type for Process IDs. + */ typedef uint32_t PROCESS_ID; -struct __PROCESS { +/*! + * Defines the prototype for process entry functions. + */ +typedef int PROCESS_MAIN(int argc, char** argv); + +/*! + * Struct that saves context information for a process. + */ +typedef struct __PROCESS { + //! Points to the next process struct in the list. struct __PROCESS *next; + + //! Unique number to identify the process with. PROCESS_ID id; + //! Address of the processes own page directory in physical memory. PHYS_ADDR page_dir; + //! Current state of the process. PROCESS_STATE state; + + //! Points to the processor instruction to be executed next. VIRT_ADDR eip; - VIRT_ADDR esp; + + //! Points to the top of the process stack. + VIRT_ADDR esp; + + //! Points to the base of the process stack. + VIRT_ADDR ebp; - uint32_t eax, ebx, ecx, edx; -}; + //! Current value of the EFLAGS register. + uint32_t eflags; -typedef struct __PROCESS PROCESS; + //! Points to the program that is to be executed by the scheduler. + PROCESS_MAIN *entry; +} PROCESS; +/*! + * Gets a process ID and returns the process struct in the list with the same ID. + * \param pid Process ID to get the process struct for. + * \return Process struct for process with ID \p pid. + */ PROCESS* get_process(PROCESS_ID pid); -PROCESS_STATE get_process_state(PROCESS_ID pid); -PROCESS_ID get_current_process(void); +/*! + * Adds a process struct to the list of all processes and assigns it a unique ID. + * \param process Pointer to the process struct to add to the list. + * \return Process ID that was assigned to the process struct. + */ PROCESS_ID add_process(PROCESS* process); + +/*! + * Removes the process struct with ID \p pid from the list of all processes. + * \param pid Process ID whose process struct is to be removed from the list. + */ void remove_process(PROCESS_ID pid); #endif
@@ -0,0 +1,62 @@
+/*! \file + * Manages the distribution of processing time among processes. + */ +#ifndef SCHEDULER_H +#define SCHEDULER_H + +#include <stdint.h> + +#include "cedos/process.h" +#include "cedos/mm/paging.h" + +/*! + * Structure of the process stack when the scheduler is executed. + */ +typedef struct { + uint32_t edi; + uint32_t esi; + uint32_t ebp; + uint32_t esp; + uint32_t edx; + uint32_t ecx; + uint32_t ebx; + uint32_t eax; + uint32_t eip; + uint32_t cs; + uint32_t eflags; +}__attribute__((packed)) SCHED_FRAME; + +/*! + * Executes a task. + */ +PROCESS_ID sched_exec(PHYS_ADDR page_dir, VIRT_ADDR eip, uint32_t eflags, VIRT_ADDR ebp); + +/*! + * Initializes the scheduler. + */ +int sched_init(void); + +/*! + * Starts the scheduler. + * \return 1 on success, 0 on failure. + */ +int sched_start(void); + +/*! + * Returns processing time to the scheduler prematurely and blocks the + * process for one unit of time. + */ +void sched_yield(void); + +/*! + * Kills the process with the specified ID. + * \param pid Process ID of the process to be killed. + * \return 1 on success, 0 on failure. + */ +int sched_kill(PROCESS_ID pid); + +/*! + * The scheduler. + */ +void sched_interrupt_c(SCHED_FRAME * volatile frame, uint32_t volatile ebp); +#endif
@@ -1,27 +0,0 @@
-/*! \file - * Manages the distribution of processing time among processes. - */ -#ifndef SCHEDULER_H -#define SCHEDULER_H - -#include <stdint.h> - -#include "cedos/process.h" -#include "cedos/mm/paging.h" - -/*! - * Executes a task. - */ -PROCESS_ID sched_exec(PHYS_ADDR page_dir, VIRT_ADDR eip, VIRT_ADDR esp); - -/*! - * Initializes the scheduler. - */ -int sched_init(void); - -/*! - * Starts the scheduler. - */ -int sched_start(void); - -#endif
@@ -0,0 +1,39 @@
+/*! \file + * Provides information about the CPUs support for SSE instructions + * and functions to enable/disable them. + */ +#ifndef SSE_H +#define SSE_H + +typedef enum { + SSE_UNSUPPORTED, + SSE_V1, + SSE_V2, + SSE_V3, + SSSE_V3, + SSE_V4_1, + SSE_V4_2, + SSE_V4_A, + SSE_V5 +} SSE_VERSION; + +typedef enum { + SSE_V5_UNSUPP, + SSE_V5_XOP, + SSE_V5_FMA4, + SSE_V5_CVT16, + SSE_V5_AVX, + SSE_V5_XSAVE, + SSE_V5_AVX_512 +} SSE_V5_FEATURES; + +SSE_VERSION sse_check_support(void); +int avx_check_support(SSE_V5_FEATURES); + +void sse_enable(void); +void sse_disable(void); + +void avx_enable(void); +void avx_disable(void); + +#endif SSE_H
@@ -1,5 +1,6 @@
#include "cedos/core.h" #include "cedos/drivers/console.h" +#include "assembly.h" CON_DRIVER *core_con;@@ -45,10 +46,10 @@ }
} void stackdump(void) { - void* stack; - __asm__ volatile ("mov %%esp, %0" : "=a" (stack)); + void *esp, *ebp; + __asm__ volatile ("mov %%esp, %0; mov %%ebp, %1" : "=m" (esp), "=m" (ebp)); printk("STACK DUMP:\n"); - memdump(stack, 0xC0400000 - (uint32_t)stack); + memdump(esp, (uint32_t)ebp - (uint32_t)esp); } void regdump(void) {@@ -76,6 +77,7 @@ printk(" ESI=%i EDI=%i ESP=%i EBP=%i\n", esi, edi, esp, ebp);
} void printk(const char* fmt, ...) { + uint32_t eflags = disable_interrupts(); va_list args; va_start(args, fmt); uint32_t index = 0;@@ -104,6 +106,8 @@ }
fmt++; } + + restore_interrupts(eflags); } void kpanic(const char* string) {
@@ -3,8 +3,10 @@ #include "cedos/drivers/console.h"
#include "cedos/pic.h" #include "cedos/core.h" #include "cedos/pit.h" +#include "cedos/sched.h" #define array_sizeof(array) (sizeof(array)/sizeof(array[0])) +#define NULL ((void*)0) #define MAKE_IDT_ENTRY(func, selector, type) { \ (uint16_t)(func), \@@ -20,14 +22,6 @@ }
INTERRUPT(breakpoint_isr, frame) { printk("BREAKPOINT WAS HIT\n"); -} - -EXCEPTION(page_fault_isr, frame, error_code) { - volatile uint32_t faulty_addr; - __asm__ volatile ("mov %%cr2, %0" : "=a" (faulty_addr)); - printk("PAGE FAULT: %i", faulty_addr); - while (1) {} - // dump registers to stdout } EXCEPTION(double_fault_isr, frame, error_code) {@@ -49,7 +43,6 @@ pic2_eoi();
} IDT_ENTRY IDT[INTERRUPT_COUNT]; -void (*INT_HANDLERS[INTERRUPT_COUNT])(void); void install_interrupt(int num, void* func, uint16_t selector, uint8_t type) { IDT[num].offset_0 = (uint16_t)((uint32_t)func);@@ -67,17 +60,19 @@ sizeof(IDT),
IDT }; +extern void* sched_interrupt; + int interrupts_init(void) { for (uint32_t i = 0; i < INTERRUPT_COUNT; i++) { if (i == 0x03) { install_interrupt(i, breakpoint_isr, 0x08, INT_GATE); } else if (i == 0x08) { install_interrupt(i, double_fault_isr, 0x08, INT_GATE); - } else if (i == 0x0e) { - install_interrupt(i, page_fault_isr, 0x08, INT_GATE); } else if (i == 0x0d) { install_interrupt(i, gpf_isr, 0x08, INT_GATE); - } else if (i >= 0x20 || i < 0x28) { + } else if (i == 0x20) { + install_interrupt(i, &sched_interrupt, 0x08, INT_GATE); + } else if (i >= 0x21 || i < 0x28) { install_interrupt(i, pic1_handler, 0x08, INT_GATE); } else if (i >= 0x28 || i < 0x30) { install_interrupt(i, pic2_handler, 0x08, INT_GATE);
@@ -2,7 +2,7 @@ #include "cedos/drivers/console.h"
#include "cedos/interrupts.h" #include "cedos/pic.h" #include "cedos/pit.h" -#include "cedos/scheduler.h" +#include "cedos/sched.h" #include "cedos/mm/paging.h" #include "cedos/core.h" #include "assert.h"@@ -23,7 +23,11 @@ printk("Initializing interrupts...");
interrupts_init(); printk("done.\n"); - printk("Setting up timer interrupts..."); + printk("Setting up paging..."); + paging_init(); + printk("done.\n"); + + printk("Activating interrupts..."); sti(); printk("done.\n");@@ -45,11 +49,32 @@ }
extern uint8_t* IDT; +void task1(void) { + //outb(0xFE, 0x64); + printk("Somebody once told me\n"); +} + +void task2(void) { + printk("The world is gonna roll me\n"); +} + +void task3(void) { + printk("I ain't the sharpest tool in the shed.\n"); + cli(); + while (1); +} + int os_main(void) { //pic_unmask_interrupt(0); infodump(); - printk("Starting scheduler."); + // create test tasks + printk("Creating tasks.\n"); + sched_exec(create_empty_page_dir(), task1, eflags(), 0xC0280000); + sched_exec(create_empty_page_dir(), task2, eflags(), 0xC0300000); + sched_exec(create_empty_page_dir(), task3, eflags(), 0xC0380000); + + printk("Starting scheduler.\n"); sched_start(); //kpanic("SIMULATED KERNEL PANIC");
@@ -2,6 +2,7 @@ #include "cedos/mm/paging.h"
#include "cedos/mm/page_allocator.h" #include "linker.h" #include "string.h" +#include "cedos/interrupts.h" #define MAKE_PAGE_ENTRY(addr, flags) (uint32_t)(((uint32_t)(addr) & 0xFFFFF000) | (flags))@@ -72,6 +73,28 @@ return 0;
} } +int map_page_to_this(PHYS_ADDR page_addr, uint32_t dir_index, uint32_t table_index, uint32_t flags) { + PAGE_DIR_ENTRY* page_dir = PAGE_DIR_MAPPED_ADDR; + PAGE_TABLE_ENTRY* page_table = PAGE_TABLE_MAPPED_ADDR(dir_index); + + int tmp = 0; + + if (!is_present(page_dir[dir_index])) { + // acquire new page table + void *new_page_table = get_free_page(); + page_dir[dir_index] = MAKE_PAGE_ENTRY(new_page_table, PAGE_TABLE_FLAGS); + } + + if (!is_present(page_table[table_index])) { + // map page + page_table[table_index] = MAKE_PAGE_ENTRY(page_addr, flags); + return 1; + } else { + // didn't works + return 0; + } +} + void mount_page_dir(PHYS_ADDR page_dir) { PAGE_DIR_ENTRY *cur_page_dir = PAGE_DIR_MAPPED_ADDR; cur_page_dir[PAGE_ENTRY_COUNT - 2] = MAKE_PAGE_ENTRY(page_dir, PAGE_TABLE_FLAGS);@@ -128,8 +151,24 @@ page_dir[PAGE_ENTRY_COUNT - 1] = MAKE_PAGE_ENTRY(page_dir_phys, PAGE_TABLE_FLAGS);
page_dir = PAGE_DIR_ALT_MAPPED_ADDR; + // identity map first 4MB + page_dir[0] = PAGE_DIR_MAPPED_ADDR[0]; + // map kernel page_dir[PAGE_DIR_INDEX(0xc0000000)] = PAGE_DIR_MAPPED_ADDR[PAGE_DIR_INDEX(0xc0000000)]; return page_dir_phys; +} + +EXCEPTION(page_fault_isr, frame, error_code) { + volatile VIRT_ADDR faulty_addr; + __asm__ volatile ("mov %%cr2, %0" : "=a" (faulty_addr)); + printk("PAGE FAULT: %i", faulty_addr); + PHYS_ADDR new_page = get_free_page(); + map_page_to_this(new_page, PAGE_DIR_INDEX(faulty_addr), PAGE_TABLE_INDEX(faulty_addr), PAGE_TABLE_FLAGS); + // dump registers to stdout +} + +int paging_init(void) { + install_interrupt(0x0e, page_fault_isr, 0x08, TRAP_GATE); }
@@ -19,6 +19,5 @@ popf();
} void pit_init(void) { - pic_unmask_interrupt(0); return 1; }
@@ -3,8 +3,8 @@
#define NULL ((void*)0) PROCESS* list_head; -PROCESS_ID current_process = 0; -PROCESS_ID next_available = 0; +//PROCESS* current_process; +//PROCESS_ID next_available = 0; PROCESS* get_process(PROCESS_ID pid) { for (PROCESS *p = list_head; p != NULL; p = p->next) {@@ -16,19 +16,6 @@ }
} return NULL; -} - -PROCESS_STATE get_process_state(PROCESS_ID pid) { - PROCESS* p = get_process(pid); - if (p == NULL) { - return PSTATE_TERMINATED; - } else { - return p->state; - } -} - -PROCESS_ID get_current_process(void) { - return current_process; } PROCESS_ID add_process(PROCESS* process) {
@@ -0,0 +1,140 @@
+#include "cedos/sched.h" +#include "cedos/process.h" +#include "cedos/mm/paging.h" +#include "cedos/drivers/console.h" +#include "cedos/core.h" +#include "cedos/interrupts.h" +#include "cedos/pit.h" +#include "cedos/pic.h" +#include "assembly.h" +#include "common.h" +#include "cedos/drivers/speaker.h" + +void* const SCHED_STACK = (void*)(0xC0400000); + +PROCESS* get_slot(void) { + static PROCESS free_slots[4]; + static uint32_t index = 0; + return &(free_slots[index++]); +} + +int sched_dispatcher(void); + +/*! + * Executes a task. + */ +PROCESS_ID sched_exec(PHYS_ADDR page_dir, VIRT_ADDR eip, uint32_t eflags, VIRT_ADDR ebp) { + PHYS_ADDR tmp_page_dir = switch_page_dir(page_dir); + PROCESS* p = get_slot(); + + // set process context + p->page_dir = page_dir; + p->eip = sched_dispatcher; + p->ebp = ebp; + p->esp = ebp - sizeof(SCHED_FRAME); + p->eflags = eflags; + p->entry = (PROCESS_MAIN*)eip; + + // setup stack + SCHED_FRAME* frame = (SCHED_FRAME*)(p->esp); + frame->eax = frame->ebx = frame->ecx = frame->edx = 0; + frame->esi = frame->edi = 0; + frame->ebp = p->ebp; + frame->esp = p->esp; + frame->eip = sched_dispatcher; + frame->cs = 0x8; + frame->eflags = eflags; + + // TODO: add file descriptors for stdin, stdout and stderr + + p->state = PSTATE_READY; + + switch_page_dir(tmp_page_dir); + + return add_process(p); +} + +PROCESS_ID current_pid; + +void sched_interrupt_c(SCHED_FRAME * volatile frame, uint32_t volatile ebp) { + //kpanic("SCHEDULER STACK INFO"); + PROCESS* process = get_process(current_pid); + + if (current_pid != 0) { + process->esp = (uint32_t)frame; + process->ebp = ebp; + process->eip = frame->eip; + process->eflags = frame->eflags; + } + + // select next process + static int pid = 0; + pid = (pid + 1) % 4; + current_pid = pid; + + printk("\n### SCHEDULER: SWITCH TO TASK %i\n", pid); + + // prepare to return to process + switch_page_dir(process->page_dir); + process = get_process(current_pid); + frame = (volatile SCHED_FRAME*)(process->esp); + ebp = process->ebp; + frame->eip = process->eip; + frame->eflags = process->eflags; + + // reset the timer + pit_setup_channel(PIT_CHANNEL_0, PIT_MODE_0, 0xFFFF); + + pic1_eoi(); + + //if (current_pid == 2) outb(0xFE, 0x64); +} + +void idle(void) { + printk("."); + while (1) { } +} + +int sched_init(void) { + // create idle process + sched_exec(create_empty_page_dir(), idle, eflags() | (1 << 9), 0xC0200000); + + return 1; +} + +int sched_start(void) { + current_pid = get_process(0); + + // perform the first timer interrupt manually + pic_unmask_interrupt(0); + INT(0x20); +} + +void sched_yield(void) { + INT(0x20); +} + +int sched_kill(PROCESS_ID pid) { + if (pid == current_pid) { + //remove_process(pid); + sched_yield(); + return 1; + } else { + //remove_process(pid); + return 1; + } +} + +int sched_dispatcher(void) { + printk("Dispatching process %i...\n", current_pid); + + PROCESS* this = get_process(current_pid); + PROCESS_MAIN* entry = this->entry; + + // enter the actual program + entry(0, (void*)0); + + printk("Process %i terminated.\n", current_pid); + + sched_kill(current_pid); +}
@@ -0,0 +1,26 @@
+.global sched_interrupt +/*! + * Prehandles scheduler interrupts by switching from process- to scheduler-stack + */ +sched_interrupt: + // preserve state of general purpose registers + pusha + + // switch to scheduler stack + mov %esp, %eax + mov %ebp, %ebx + mov $SCHED_STACK, %esp + mov $SCHED_STACK, %ebp + + // pass process stack as arguments + push %ebx + push %eax + call sched_interrupt_c + pop %eax + pop %ebx + + // restore process stack and state of registers + mov %eax, %esp + mov %ebx, %ebp + popa + iret
@@ -1,68 +0,0 @@
-#include "cedos/scheduler.h" -#include "cedos/process.h" -#include "cedos/mm/paging.h" -#include "cedos/drivers/console.h" -#include "cedos/core.h" -#include "cedos/interrupts.h" -#include "cedos/pit.h" -#include "cedos/pic.h" -#include "assembly.h" -#include "cedos/drivers/speaker.h" - -PROCESS* get_slot(void) { - static PROCESS free_slots[4]; - static uint32_t index = 0; - return &(free_slots[index++]); -} - -/*! - * Executes a task. - */ -PROCESS_ID sched_exec(PHYS_ADDR page_dir, VIRT_ADDR eip, VIRT_ADDR esp) { - PROCESS* p = get_slot(); - - p->page_dir = page_dir; - p->eip = eip; - p->esp = esp; - - // TODO: add file descriptors for stdin, stdout and stderr - - p->state = PSTATE_READY; - - return add_process(p); -} - -INTERRUPT(sched_interrupt, frame) { - printk("--#"); - - // reset the timer - pit_setup_channel(PIT_CHANNEL_0, PIT_MODE_0, 0xFFFF); - - uint32_t big_ben[] = { 415, 330, 370, 247, 247, 370, 415, 330 }; - static int i = 0; - - bios_sp.make_sound(big_ben[(i / 8) % (sizeof(big_ben)/sizeof(big_ben[0]))], 1); - i++; - - pic1_eoi(); -} - -int sched_init(void) { - // TODO: create and start idle process - - //sched_exec(create_empty_page_dir(), (void*)0, (void*)0); - - // install timer interrupt - install_interrupt(0x20, sched_interrupt, 0x08, INT_GATE); - - return 1; -} - -int sched_start(void) { - // perform the first timer interrupt manually - INT(0x20); -} - -int sched_dispatcher(void) { - -}
@@ -1,1 +1,1 @@
-qemu -drive index=0,if=floppy,format=raw,file=build/base.img -m 64 -monitor stdio -no-reboot -d int,cpu_reset,exec,in_asm 2> debug/err.log+qemu -drive index=0,if=floppy,format=raw,file=build/base.img -m 64 -monitor stdio -no-reboot -d int,cpu_reset,exec,in_asm 2> /dev/null