Ich habe vor ein paar Tagen ein bisschen weiter mit meinen Kernel gemacht und schon habe ich das nächste Problem.
Es geht um Multitasking im Usermode.
Interrupts und Multitasking mit Thread\Prozess-Strukturen haben bereits funktioniert, doch im Ring 3 hängt sich QEmu kommentarlos auf und Bochs rebootet.
Erstmal habe ich schon ein paar Verständnisprobleme:
- Wozu brauche ich diesen UserStack? Es existiert ja bereits ein Threadeigener Stack, also wozu nocheiner?
- Weshalb wird dieses TSS gebraucht? Die UserStack-Adresse übergebe ich ja bereits im CPU-State
Nun zum konkreten Problem und Code:
Ich vermute, dass die Emulatoren beim GDT abstürzen.
Daher dieser Code: ("
gdt.c")
#include "gdt.h"
#include <stdint.h>
#include "console.h"
#include "tss.h"
#define GDT_FLAG_DATASEG 0x02
#define GDT_FLAG_CODESEG 0x0a
#define GDT_FLAG_TSS 0x09
#define GDT_FLAG_SEGMENT 0x10
#define GDT_FLAG_RING0 0x00
#define GDT_FLAG_RING3 0x60
#define GDT_FLAG_PRESENT 0x80
#define GDT_FLAG_4K 0x800
#define GDT_FLAG_32_BIT 0x400
#define GDT_ENTRIECOUNT 5
static uint64_t Gdt_Gdt[GDT_ENTRIECOUNT];
static inline void Gdt_SetEntry(int i, unsigned int Base, unsigned int Limit, int Flags)
{
Gdt_Gdt[i] = Limit & 0xffffLL;
Gdt_Gdt[i] |= (Base & 0xffffffLL) << 16;
Gdt_Gdt[i] |= (Flags & 0xffLL) << 40;
Gdt_Gdt[i] |= ((Limit >> 16) & 0xfLL) << 48;
Gdt_Gdt[i] |= ((Flags >> 8 )& 0xffLL) << 52;
Gdt_Gdt[i] |= ((Base >> 24) & 0xffLL) << 56;
}
void Gdt_Init()
{
Console_WriteLine("Lade GDT");
struct
{
uint16_t Limit;
void* Ptr;
} __attribute__((packed)) GdtP = {.Limit = GDT_ENTRIECOUNT * 8 - 1, .Ptr = Gdt_Gdt, };
// GDT-Eintraege aufbauen
Gdt_SetEntry(1, 0, 0xfffff, GDT_FLAG_SEGMENT | GDT_FLAG_32_BIT | GDT_FLAG_CODESEG | GDT_FLAG_4K | GDT_FLAG_PRESENT);
Gdt_SetEntry(2, 0, 0xfffff, GDT_FLAG_SEGMENT | GDT_FLAG_32_BIT | GDT_FLAG_DATASEG | GDT_FLAG_4K | GDT_FLAG_PRESENT);
Gdt_SetEntry(3, 0, 0xfffff, GDT_FLAG_SEGMENT | GDT_FLAG_32_BIT | GDT_FLAG_CODESEG | GDT_FLAG_4K | GDT_FLAG_PRESENT | GDT_FLAG_RING3);
Gdt_SetEntry(4, 0, 0xfffff, GDT_FLAG_SEGMENT | GDT_FLAG_32_BIT | GDT_FLAG_DATASEG | GDT_FLAG_4K | GDT_FLAG_PRESENT | GDT_FLAG_RING3);
Gdt_SetEntry(5, (uint32_t)(&Tss_Tss), sizeof(Tss), GDT_FLAG_TSS | GDT_FLAG_PRESENT | GDT_FLAG_RING3);
// GDT neu laden
asm volatile("lgdt %0" : : "m" (GdtP));
// Segmentregister neu laden, damit die neuen GDT-Eintraege auch wirklich
// benutzt werden
asm volatile(
"mov $0x10, %ax;"
"mov %ax, %ds;"
"mov %ax, %es;"
"mov %ax, %ss;"
"ljmp $0x8, $.1;"
".1:"
);
// Taskregister neu laden
asm volatile("ltr %%ax" : : "a" (5 << 3));
Console_WriteLine("GDT bereit");
}und der Code von "
tss.h":
#ifndef TSS
#define TSS
#include <stdint.h>
#include <stddef.h>
typedef struct TssStruct
{
uint32_t prevtasklink;
uint32_t esp0;
uint32_t ss0;
uint32_t esp1;
uint32_t ss1;
uint32_t esp2;
uint32_t ss2;
uint32_t cr3;
uint32_t eip;
uint32_t eflags;
uint32_t eax;
uint32_t ecx;
uint32_t edx;
uint32_t ebx;
uint32_t esp;
uint32_t ebp;
uint32_t esi;
uint32_t edi;
uint32_t es;
uint32_t cs;
uint32_t ss;
uint32_t ds;
uint32_t fs;
uint32_t gs;
uint32_t ldt;
uint32_t iomapbaseaddress;
} __attribute__((packed)) Tss;
extern Tss Tss_Tss;
#endifIn "
tss.c" setze ich eigentlich bis auf "
ss0" bloß alles auf Null. "
ss0" wird auf 16 gesetzt.
Nochwas anderes was ich mich seit einiger Zeit frage:
Kann man eigentlich davon ausgehen dass der Gcc das "
Gdt_SetEntry" komplett so inlined und optimiert, dass es wie eine einfache Zuweißung einer LL-Konstante im Assemblercode dasteht?
Ich hoffe ihr könnt mir, wie schon bei meinen ersten Problem, sehr kompetent weiterhelfen.