| Zeile 20: | Zeile 20: | ||
gdt_create_descriptor ( | gdt_create_descriptor ( | ||
n, //die Nummer des Descriptor-Eintrags | n, //die Nummer des Descriptor-Eintrags | ||
| − | ( | + | (uint32_t) &task_state_segment, //Adresse |
sizeof(struct tss)-1, //Länge | sizeof(struct tss)-1, //Länge | ||
0, //flags | 0, //flags | ||
| Zeile 74: | Zeile 74: | ||
struct tss task_state_segments[NUM_TASKS]; | struct tss task_state_segments[NUM_TASKS]; | ||
| − | + | uint32_t stacks[NUM_TASKS * 7]; // (1) | |
/*** Hilfsfunktionen ***/ | /*** Hilfsfunktionen ***/ | ||
| − | void create_task ( | + | void create_task ( uint32_t n, //die ID/Nummer des Tasks |
| − | + | uint16_t cs, uint16_t ss, uint16_t ds, | |
| − | + | uint32_t adr_code, uint32_T adr_stack) | |
{ | { | ||
if(n > NUM_TASKS) return; | if(n > NUM_TASKS) return; | ||
| Zeile 90: | Zeile 90: | ||
task_state_segments[n].eflags = 0x1600; // (4) | task_state_segments[n].eflags = 0x1600; // (4) | ||
task_state_segments[n].ss0 = 0x10; // (2) | task_state_segments[n].ss0 = 0x10; // (2) | ||
| − | task_state_segments[n].esp0 = ( | + | task_state_segments[n].esp0 = (uint32_t) &stacks[n*7]; // (3) |
} | } | ||
</c> | </c> | ||
| Zeile 110: | Zeile 110: | ||
gdt_create_descriptor ( | gdt_create_descriptor ( | ||
i+TSS_SEL_BASE, | i+TSS_SEL_BASE, | ||
| − | ( | + | (uint32_TD) &task_state_segment[i], |
sizeof(struct tss)-1, | sizeof(struct tss)-1, | ||
0, | 0, | ||
| Zeile 125: | Zeile 125: | ||
struct task | struct task | ||
{ | { | ||
| − | + | uint8_t present; | |
| − | + | uint8_t priority; | |
}__attribute__((packed)); | }__attribute__((packed)); | ||
| Zeile 144: | Zeile 144: | ||
<c> | <c> | ||
| − | + | uint32_t current_task_id=0; | |
| − | + | uint16_t tss_sel; | |
void scheduler() | void scheduler() | ||
{ | { | ||
| Zeile 157: | Zeile 157: | ||
} | } | ||
</c> | </c> | ||
| − | Jetzt muss zu diesem Task | + | Jetzt muss zu diesem Task gewechselt werden, indem zu dessen TSS-Selektor gesprungen wird. Es empfiehlt sich diesen Teil in Assembler zu schreiben, da die ISR auch aus einem Assemblerstub heraus aufgerufen werden sollte. Dieser sieht dann so aus: |
<asm> | <asm> | ||
| Zeile 214: | Zeile 214: | ||
Wie bereits erwähnt ist es möglich, auch das Scheduling zum Teil von der Hardware ausführen zu lassen. Dafür muss man den PIT-Interrupt als Task-Gate anlegen, die auf das TSS des nächsten Tasks zeigt. Dadurch wechselt die CPU automatisch zum nächsten Task. | Wie bereits erwähnt ist es möglich, auch das Scheduling zum Teil von der Hardware ausführen zu lassen. Dafür muss man den PIT-Interrupt als Task-Gate anlegen, die auf das TSS des nächsten Tasks zeigt. Dadurch wechselt die CPU automatisch zum nächsten Task. | ||
| − | Dann setzt man Bit 0 im letzten | + | Dann setzt man Bit 0 im letzten DWord jeder TSS (Debug-Trap-Bit), wodurch jedes mal eine Debugexception nach einem Task-Switch ausgelöst wird, in der man dann das Task-Gate auf den nächsten Task zeigen lässt. |
Diese Methode ist allerdings sehr kompliziert, man kommt dennoch nicht um etwas Software-Scheduling herum, und ausprobiert habe ich es auch noch nicht. | Diese Methode ist allerdings sehr kompliziert, man kommt dennoch nicht um etwas Software-Scheduling herum, und ausprobiert habe ich es auch noch nicht. | ||