diff --git a/prototypes/base/include/cpustate.hpp b/prototypes/base/include/cpustate.hpp index 45aca24..94f1503 100644 --- a/prototypes/base/include/cpustate.hpp +++ b/prototypes/base/include/cpustate.hpp @@ -20,6 +20,6 @@ struct CpuState uint32_t eflags; uint32_t esp; uint32_t ss; -}; +} __attribute__((packed)); static_assert(sizeof(CpuState) == 56, "CpuState must be 56 bytes large."); \ No newline at end of file diff --git a/prototypes/base/include/driver/keyboard.hpp b/prototypes/base/include/driver/keyboard.hpp index 7a94caa..5e1d725 100644 --- a/prototypes/base/include/driver/keyboard.hpp +++ b/prototypes/base/include/driver/keyboard.hpp @@ -9,7 +9,7 @@ namespace driver public Driver { private: - static void dispatchIRQ(CpuState *cpu); + static void dispatchIRQ(CpuState *&cpu); public: Keyboard(); diff --git a/prototypes/base/include/idt.hpp b/prototypes/base/include/idt.hpp index 965b38d..7654b10 100644 --- a/prototypes/base/include/idt.hpp +++ b/prototypes/base/include/idt.hpp @@ -48,7 +48,7 @@ static_assert(sizeof(InterruptDescriptor) == 8, "InterruptDescriptor must be 8 b class Interrupt { friend class IDT; - using Handler = void (*)(CpuState *cpu); + using Handler = void (*)(CpuState * & cpu); private: bool isEnabled; Handler handler; @@ -70,7 +70,7 @@ private: static InterruptDescriptor descriptors[length]; IDT() = delete; - static void dispatch(CpuState *cpu); + static CpuState * dispatch(CpuState *cpu); static void setupPIC(); public: diff --git a/prototypes/base/init.cpp b/prototypes/base/init.cpp index 01a40c5..8300e2d 100644 --- a/prototypes/base/init.cpp +++ b/prototypes/base/init.cpp @@ -105,6 +105,108 @@ static void dump_elf(elf::Header *header) } } +void delay() +{ + for(volatile uint32_t i = 0; i < 0x1000000; i++); +} + +void task_a(void) +{ + while (1) { + Console::main.put('A'); + delay(); + } +} + +void task_b(void) +{ + while (1) { + Console::main.put('B'); + delay(); + } +} + +static uint8_t stack_a[4096]; +static uint8_t stack_b[4096]; + +/* + * Jeder Task braucht seinen eigenen Stack, auf dem er beliebig arbeiten kann, + * ohne dass ihm andere Tasks Dinge ueberschreiben. Ausserdem braucht ein Task + * einen Einsprungspunkt. + */ +CpuState* init_task(uint8_t* stack, void (*entry)()) +{ + /* + * CPU-Zustand fuer den neuen Task festlegen + */ + CpuState new_state; + { + new_state.eax = 0; + new_state.ebx = 0; + new_state.ecx = 0; + new_state.edx = 0; + new_state.esi = 0; + new_state.edi = 0; + new_state.ebp = 0; + //new_state..esp = unbenutzt (kein Ring-Wechsel) + new_state.eip = reinterpret_cast(entry); + + /* Ring-0-Segmentregister */ + new_state.cs = 0x08; + //new_state..ss = unbenutzt (kein Ring-Wechsel) + + /* IRQs einschalten (IF = 1) */ + new_state.eflags = 0x202; + } + + /* + * Den angelegten CPU-Zustand auf den Stack des Tasks kopieren, damit es am + * Ende so aussieht als waere der Task durch einen Interrupt unterbrochen + * worden. So kann man dem Interrupthandler den neuen Task unterschieben + * und er stellt einfach den neuen Prozessorzustand "wieder her". + */ + CpuState* state = (CpuState*) (stack + 4096 - sizeof(new_state)); + *state = new_state; + + return state; +} + +static int current_task = -1; +static int num_tasks = 2; +static CpuState* task_states[2]; + +void init_multitasking(void) +{ + task_states[0] = init_task(stack_a, task_a); + task_states[1] = init_task(stack_b, task_b); +} + +/* + * Gibt den Prozessorzustand des naechsten Tasks zurueck. Der aktuelle + * Prozessorzustand wird als Parameter uebergeben und gespeichert, damit er + * beim naechsten Aufruf des Tasks wiederhergestellt werden kann + */ +void schedule(CpuState *& cpu) +{ + /* + * Wenn schon ein Task laeuft, Zustand sichern. Wenn nicht, springen wir + * gerade zum ersten Mal in einen Task. Diesen Prozessorzustand brauchen + * wir spaeter nicht wieder. + */ + if (current_task >= 0) { + task_states[current_task] = cpu; + } + + /* + * Naechsten Task auswaehlen. Wenn alle durch sind, geht es von vorne los + */ + current_task++; + current_task %= num_tasks; + + /* Prozessorzustand des neuen Tasks aktivieren */ + cpu = task_states[current_task]; +} + extern "C" void init(Structure const & data) { Console::main @@ -198,13 +300,16 @@ extern "C" void init(Structure const & data) timer.install(); keyboardDriver.install(); scheduler.install(); - Console::main << "Drivers installed.\n"; + IDT::interrupt(0x20) = Interrupt(schedule); + + init_multitasking(); + + Console::main << "Drivers installed.\n"; asm volatile("sti"); Console::main << "Interrupts enabled.\n"; - if(data.modules.length > 0) { diff --git a/prototypes/base/interrupts.S b/prototypes/base/interrupts.S index 832f89a..8bb2215 100644 --- a/prototypes/base/interrupts.S +++ b/prototypes/base/interrupts.S @@ -39,7 +39,9 @@ isr_handler: // Calls interrupt handler with CPU state as argument push %esp call IDT_DISPATCH - add $4, %esp + // add $4, %esp + mov %eax, %esp + // Restore CPU State pop %eax diff --git a/prototypes/base/src/idt.cpp b/prototypes/base/src/idt.cpp index 99a8040..18ec861 100644 --- a/prototypes/base/src/idt.cpp +++ b/prototypes/base/src/idt.cpp @@ -63,8 +63,10 @@ void IDT::setupPIC() slavePIC.maskInterrupts(0x00); } -void IDT::dispatch(CpuState *cpu) +CpuState * IDT::dispatch(CpuState *cpu) { + bool ackMaster = cpu->interrupt >= 0x20 && cpu->interrupt <= 0x2F; + bool ackSlave = cpu->interrupt >= 0x28; Interrupt &intr = IDT::interrupts[cpu->interrupt]; if(intr.isEnabled) { (*intr.handler)(cpu); @@ -73,12 +75,13 @@ void IDT::dispatch(CpuState *cpu) } // ACK interrupts - if (cpu->interrupt >= 0x20 && cpu->interrupt <= 0x2F) { - if(cpu->interrupt >= 0x28) { + if (ackMaster) { + if(ackSlave) { slavePIC.sendEndOfInterrupt(); } masterPIC.sendEndOfInterrupt(); } + return cpu; } diff --git a/prototypes/base/src/keyboard.cpp b/prototypes/base/src/keyboard.cpp index a42c0b6..d0e5114 100644 --- a/prototypes/base/src/keyboard.cpp +++ b/prototypes/base/src/keyboard.cpp @@ -14,7 +14,7 @@ namespace driver IDT::interrupt(0x21) = Interrupt(Keyboard::dispatchIRQ); } - void Keyboard::dispatchIRQ(CpuState *cpu) + void Keyboard::dispatchIRQ(CpuState *&cpu) { Console::main << "keyboard! "; } diff --git a/prototypes/base/src/timer.cpp b/prototypes/base/src/timer.cpp index d58ba47..760b0f6 100644 --- a/prototypes/base/src/timer.cpp +++ b/prototypes/base/src/timer.cpp @@ -12,7 +12,7 @@ namespace driver void Timer::install() { - IDT::interrupt(0x20) = Interrupt([](auto *) { counter++; }); + IDT::interrupt(0x20) = Interrupt([](auto *&) { counter++; }); } void Timer::reset()