Also das mit dem Quellcode ist eher schwierig weils ziemlich viel ist an dem es liegen könnte, aber ich kann den relevanten Teil mal hier hochladen, die Logging Funktion meines Emulators kann ich leider nicht nutzen, weil ich einen echten PC als Emulator verwende und von einem USB-Stick boote (Die quemu installation ist desöfteren gescheitert und ein ordentliches Image hab ich dank fehlendem grub legacy auch nicht hingekriegt).
Aber ich habe es jetz hinreichend getestet, also wenn nur ein Task läuft ist alles in Ordnung, aber sobald ich mehrere tasks laufen habe, eigtl immer genau dann wenn der Taskswitch stattfindet, also beim Timerinterrupt, bekomme ich einen General Protection Fault, der von meinem Interrupt Handler bearbeitet wird und dann hält mein Kernel gewollt an.
Anbei meine task.c:
// Sourcecode File for Headerdatei task.h
#include "console.h"
#include "pmm.h"
#include "task.h"
//#include "stdint.h"
#include <stdint.h>
#include <stddef.h>
struct task {
struct cpu_state* cpu_state; // Pointer auf obige Struktur
struct task* next;
};
static struct task* first_task = NULL; // Nullzeiger
static struct task* current_task = NULL; // Nullzeiger
// Jeder Task: Einsprungpunkt -> entry; Stack -> stack;
struct task* init_task(void* entry)
{
uint8_t* stack = pmm_alloc(); // unsigned char (uint8_t*) weil void* typlos
uint8_t* user_stack = pmm_alloc(); // -"-
// CPU-Zustand fuer den neuen Task festlegen
struct cpu_state new_state = {
.eax = 0,
.ebx = 0,
.ecx = 0,
.edx = 0,
.esi = 0,
.edi = 0,
.ebp = 0,
.esp = (uint32_t) user_stack + 4096, // Ring Wechsel auf Ring 3
.eip = (uint32_t) entry,
// Ring-0-Segmentregister : 0x08
.cs = 0x1b,
.ss = 0x20 | 0x03, // benutzt weil Ringwechsel auf 3
// IRQs einschalten (IF = 1)
.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".
*/
struct cpu_state* state = (void*) (stack + 4096 - sizeof(new_state));
*state = new_state;
/*
* Neue Taskstruktur anlegen und in die Liste einhaengen
*/
struct task* task = pmm_alloc();
task->cpu_state = state;
task->next = first_task;
first_task = task;
return task;
}
// Aktueller Zustand wird als Parameteuebergeben und gespeichert.
// Neuer Prozessorzusatand wirdurueck gegeben
struct cpu_state* schedule(struct cpu_state* cpu)
{
// Wenn Task vorhanden, Zusatand sichern. Wenn nicht Prozessorzustand vernachla$
if (current_task != NULL) {
current_task->cpu_state = cpu;
}
// Naechsten Task auswaehlen -> schleife von vorne wenn durch
if (current_task == NULL) {
current_task = first_task;
}
else {
current_task = current_task->next;
if (current_task == NULL) {
current_task = first_task;
}
}
// Prozessorzustand des neuen Task zurueckgeben
cpu = current_task->cpu_state;
return cpu;
}
Und hier mal mein Interrupt Handler:
struct cpu_state* handle_interrupt(struct cpu_state* cpu)
{
struct cpu_state* new_cpu = cpu;
if (cpu->intr <= 0x1f) {
// kprintf("Exception %d, Kernel angehalten!\n", cpu->intr);
kputs("Exception: ");
kputn(cpu->intr, 10);
kputs("\n Kernel angehalten! \n");
// Hier den CPU-Zustand ausgeben
while(1) {
// Prozessor anhalten
asm volatile("cli; hlt");
}
} else {
// Hier den Hardwareinterrupt behandeln
// kputs("Hardwareinterrupt");
if (cpu->intr >= 0x20 && cpu->intr <= 0x2f) {
if (cpu->intr == 0x20) {
kputs("Timerinterrupt vor");
new_cpu = schedule(cpu); // #debug
set_tss_entry(1, (unsigned long) (new_cpu + 1)); // #debug
kputs("Timerinterrupt nach");
}
if (cpu->intr == 0x21) {
kputs("Tastaturinterrupt");
}
if (cpu->intr >= 0x28) {
outb(0xa0, 0x20); // End of Interrupt: Slave PIC
}
outb(0x20, 0x20); // End of Interrupt: Master PIC
}
}
return new_cpu;
}
Die Funktion set_tss_entry erstellt einen Eintrag im TSS das in einer anderen Datei liegt. Diese Funktion wird über eine Headerdatei bereitgestellt und funktioniert auch.
Die kputs() im Timerinterrupt sind nur zu Debuggingzwecken vorhanden.
Vg s137