Autor Thema: Software Multitasking  (Gelesen 30487 mal)

TheThing

  • Beiträge: 105
    • Profil anzeigen
Gespeichert
« Antwort #40 am: 08. May 2009, 14:04 »
Mal wieder ne Frage von mir:
Ich les mir gerade den source von Linux 0.01 durch. Meine Frage: Nutzt die Version des Kernels Software oder Hardware Multitasking?

bluecode

  • Beiträge: 1 391
    • Profil anzeigen
    • lightOS
Gespeichert
« Antwort #41 am: 08. May 2009, 19:28 »
Hardware.
lightOS
"Überlegen sie mal 'nen Augenblick, dann lösen sich die ganzen Widersprüche auf. Die Wut wird noch größer, aber die intellektuelle Verwirrung lässt nach.", Georg Schramm

TheThing

  • Beiträge: 105
    • Profil anzeigen
Gespeichert
« Antwort #42 am: 09. May 2009, 19:16 »
ok, und ab welcher Version nutzt Linux Software Multitasking?

bluecode

  • Beiträge: 1 391
    • Profil anzeigen
    • lightOS
Gespeichert
« Antwort #43 am: 09. May 2009, 21:12 »
Das werde ich sicherlich nicht nachschauen, außerdem glaube ich nicht, dass jemand Linux so genau kennt. :roll:
Du solltest lieber eine Frage zu Software-Multitasking stellen, wenn du da was nicht verstanden hast. Außerdem gibt es genug Hobby-OS mit SW-Multitasking (tyndur, lightOS, pedigree um nur einige zu nennen), bei denen du eventuell mehr lernen könntest.
lightOS
"Überlegen sie mal 'nen Augenblick, dann lösen sich die ganzen Widersprüche auf. Die Wut wird noch größer, aber die intellektuelle Verwirrung lässt nach.", Georg Schramm

ehenkes

  • Gast
Gespeichert
« Antwort #44 am: 11. May 2009, 00:58 »
Ich habe noch nicht verstanden, wie Tyndur den SW Taskswitch im IRQ Stub durchführt.

Ich verwende folgenden asm Stub z.B. für den Timer Handler, in dem dann auch der Task Switch ablaufen soll, den ich in C versucht habe und der mit einer Exception (z.B. PF, GPF) oder mit einem Absturz endet:
extern _irq_handler

irq_common_stub:
    ;pusha
    push edi
push esi
push ebp
push esp
push ebx
push edx
push ecx
push eax

push ds
    push es
    push fs
    push gs

    mov ax, 0x10
    mov ds, ax
    mov es, ax
    mov fs, ax
    mov gs, ax
   
mov eax, esp
    push eax
    mov eax, _irq_handler
    call eax
    pop eax

    pop gs
    pop fs
    pop es
    pop ds
   
;popa
pop eax
pop ecx
pop edx
pop ebx
pop esp
pop ebp
pop esi
pop edi

    add esp, 8
sti
    iret

Ist da etwas falsch, was die Exception auslöst?
Task-Switch in C/inline-asm:
void task_switch()
{
    // If we haven't initialised tasking yet, just return.
    if(!current_task) return;

    cli();
    // Read esp, ebp now for saving later on.
    ULONG esp, ebp, eip;

    eip = read_eip();
    if(eip == 0x12345) return; // Have we just switched tasks?

    asm volatile("mov %%esp, %0" : "=r"(esp));
    asm volatile("mov %%ebp, %0" : "=r"(ebp));

    //old_task
    current_task->eip = eip;
    current_task->esp = esp;
    current_task->ebp = ebp;

    //--------------------------
    //old_task ==> new_task
    current_task = current_task->next;
    if(!current_task) current_task = ready_queue;

    // new_task
    current_directory = current_task->page_directory;
    set_kernel_stack(current_task->kernel_stack+KERNEL_STACK_SIZE);
    tss_entry.eip = eip = current_task->eip; //TEST
    tss_entry.cr3 = current_directory->physicalAddr; //TEST
    esp = current_task->esp;
    ebp = current_task->ebp;
    //TSS_log(&tss_entry); //TEST

    asm volatile("       \
    cli;                 \
    mov %%ebx, %%esp;    \
    mov %%edx, %%ebp;    \
    mov %%eax, %%cr3;    \
    mov $0x12345, %%eax; \
    sti;                 \
    jmp *%%ecx;          "
               : : "c"(eip), "b"(esp), "d"(ebp), "a"(current_directory->physicalAddr):"%edi","%esi" );
}
Ideen, wie ich das doch noch zum Laufen bekommen kann? Ich verwende nur eine TSS.

Wie macht Tyndur den eigentlichen Task-Switch im Timer-Handler?




Jidder

  • Administrator
  • Beiträge: 1 625
    • Profil anzeigen
Gespeichert
« Antwort #45 am: 11. May 2009, 10:32 »
Du hast uns versprochen, dass du es mal anders versuchst ;)
Daher mache ich bezüglich des Task Switch nun einen kompletten Roll-back.

Was ich ja interessant finde, ist dass du esp als Parameter übergibst, aber gar nicht benutzt.


tyndur macht das so:
irq_common:
    1. Register sichern (ähnlich wie du)
    2. esp der C-Routine übergeben (push esp)
    3. C-Routine aufrufen (call handle_int)
    4. Rückgabewert nach esp laden (mov eax, esp)
    5. Register wiederherstellen, Stack ausgleichen
    6. iret

// Prinzip der C-Routine:
unsigned int handle_int(unsigned int esp)
{
    if(kein Taskwechsel nötig)
        return esp;

    // Taskwechsel:
    current_task->esp = esp; // esp sichern
    current_task = schedule(); // neuen Task bestimmen
    return current_task->esp; // esp zurückgeben
}

Der Code nutzt aus, dass eip und ebp bereits auf dem Stack liegen. Die werden wie alle Register gesichert und wiederhergestellt. Außerdem ist es einfacher nicht solchen verzwickten Assembler-Code mit C-Code zu mischen. Voraussetzung dafür sind konsistente Interrupt-Handler bezüglich des Stack-Layouts, das heißt alle deine Stubs müssen "kompatibel" zueinander sein. Das "add esp, 8" deutet an, dass dein Kernel das wohl bereits so macht.
« Letzte Änderung: 11. May 2009, 10:36 von PorkChicken »
Dieser Text wird unter jedem Beitrag angezeigt.

kevin

  • Administrator
  • Beiträge: 2 767
    • Profil anzeigen
Gespeichert
« Antwort #46 am: 11. May 2009, 18:36 »
Und der "eigentliche Taskswitch" ist dann das mov %eax, %esp im allgemeinen Interrupthandler-Code.
Thou shalt not follow the NULL pointer, for chaos and madness await thee at its end.

ehenkes

  • Gast
Gespeichert
« Antwort #47 am: 11. May 2009, 18:48 »
Zitat
Du hast uns versprochen, dass du es mal anders versuchst
Ich war die letzte Zeit noch mit dem Thema Memory Management beschäftigt. Das läuft jetzt halbwegs stabil.

Nun möchte ich ein stabiles und transparentes Tasking System aufbauen. Daher vielen Dank für die obige Darstellung! Diese Interrupt-Geschichte ist bei mir leider noch ziemlich verworren, funktioniert aber.

Ich habe es mit eurer Routine versucht, hat aber wie erwartet noch nicht geklappt, weil der Handler-Mechanismus mit dem return esp bzw. return current_task->esp noch eingebaut werden muss.

Könnte bitte jemand prüfen, ob die Intel Syntax im asm-Stub so überhaupt richtig übertragen wurde:
extern _irq_handler

irq_common_stub:
    push eax
    push gs
    push fs
   
    xor eax,eax
    mov ax, es
    push eax
   
    mov ax,ds
    push eax
   
    push ebp
    push edi
    push esi
    push edx
    push ecx
    push ebx
   
    cld
    mov ax, 0x10
    mov ds, ax
    mov es, ax
   
    push esp
    call _irq_handler
    mov esp, eax

    pop ebx
    pop ecx
    pop edx
    pop esi
    pop edi
    pop ebp

    pop eax
    mov ds,ax
    pop eax
    mov es,ax   
    pop fs
    pop gs
    pop eax
   
    add esp, 8
    iret

Das mit dem handle_int(esp) leuchtet mir ein. Aber wo gehört dies genau hin? Ich habe in meinem OS einen irq_handler, der momentan so aussieht, und vom asm-Stub aufgerufen wird:
void irq_handler(struct regs* r)
{
    void (*handler)(struct regs* r);
    handler = irq_routines[r->int_no - 32];
    if (handler) { handler(r); }
    if (r->int_no >= 40) { outportb(0xA0, 0x20); }
    outportb(0x20, 0x20);
}

Die Verbindung von dort zum Timer Handler:
void irq_install_handler(int irq, void (*handler)(struct regs* r))
{
    irq_routines[irq] = handler;
}

void timer_install()
{
    irq_install_handler(0, timer_handler);
    systemTimer_setFrequency( 100 );
}

void timer_handler(struct regs* r)
{
    ++timer_ticks;
    task_switch();  // Das gehört ja jetzt wohl an andere Stelle?
}
Wie baut man da eure Rückgabe des alten bzw. neuen esp ein?

Die struct regs sieht so aus:
/* This defines what the stack looks like after an ISR was running */
typedef struct regs
{
    ULONG gs, fs, es, ds;
    ULONG edi, esi, ebp, esp, ebx, edx, ecx, eax;
    ULONG int_no, err_code;
    ULONG eip, cs, eflags, useresp, ss;
}registers_t;
Die muss wohl auch umgebaut werden, damit man esp auch wirklich erwischt. Das ist leider einer meiner schwächsten Ecken.  :roll:
Daher bitte möglichst viele Details, damit es möglichst bald klappt.
Wenn man diese regs Strukutur richtig aufbaut, wird das r->esp doch bis in den Timer Handler getragen. Der ist aber void.  :?

Zitat
Und der "eigentliche Taskswitch" ist dann das mov %eax, %esp im allgemeinen Interrupthandler-Code.
Dieser Mechanismus leuchtet mir ein, ich habe aber noch Probleme dies ohne Komplettumbau in mein Gefrickel zu übertragen. 


« Letzte Änderung: 11. May 2009, 18:55 von ehenkes »

kevin

  • Administrator
  • Beiträge: 2 767
    • Profil anzeigen
Gespeichert
« Antwort #48 am: 11. May 2009, 18:52 »
Du hast kein task_switch(), das auf der Stelle umschaltet. Du kannst z.B. ein current_task = current_task->next machen und wenn dein _irq_handler dann return current_task->esp macht, könnte es langsam Formen annehmen.
Thou shalt not follow the NULL pointer, for chaos and madness await thee at its end.

ehenkes

  • Gast
Gespeichert
« Antwort #49 am: 11. May 2009, 18:57 »
Sorry, aber das mit dem "auf der Stelle umschalten" verstehe ich konzeptionell leider noch nicht. Ich möchte doch im timer_handler umschalten und nicht bei jedem interrupt, oder sehe ich das verkehrt? 
« Letzte Änderung: 11. May 2009, 19:17 von ehenkes »

kevin

  • Administrator
  • Beiträge: 2 767
    • Profil anzeigen
Gespeichert
« Antwort #50 am: 11. May 2009, 19:19 »
Jein. Du möchtest wirklich umschalten, wenn du einen Timer kriegst, eventuell bei IRQs und vermutlich auch bei manchen Syscalls. Dort machst du dann die Änderung von current_task. Bei allen anderen Fällen bleibt current_task eben wie es ist und dein Interrupthandler schaltet wieder zu dem gleichen Task um, in dem du sowieso schon warst.
Thou shalt not follow the NULL pointer, for chaos and madness await thee at its end.

ehenkes

  • Gast
Gespeichert
« Antwort #51 am: 11. May 2009, 19:37 »
OK, ich versuche es auf meinen Code zu übertragen:
In diese Funktion ...
void irq_handler(struct regs* r)
{
    void (*handler)(struct regs* r);
    handler = irq_routines[r->int_no - 32];
    if (handler) { handler(r); }
    if (r->int_no >= 40) { outportb(0xA0, 0x20); }
    outportb(0x20, 0x20);
}
... würde esp mit eurem asm Stub als erster Parameter in regs übertragen, weil dieses Argument zuletzt auf den Stack geschoben wurde, richtig? Hat tyndur auch so eine Register Struktur zur Auswertung von exceptions? Diese könnte ich ja übernehmen, damit alles zusammen passt.
« Letzte Änderung: 11. May 2009, 19:40 von ehenkes »

Jidder

  • Administrator
  • Beiträge: 1 625
    • Profil anzeigen
Gespeichert
« Antwort #52 am: 11. May 2009, 19:48 »
ja, hat es. struct int_stack_frame. Das entspricht wohl genau deinem struct regs. Allerdings eignet sich das nicht, um esp zu ändern, weil bei einem popa esp nicht geladen wird. Deswegen hat handle_int den Rückgabewert für esp.

Dein irq_common_stub passt übrigens nicht zu deinem struct regs. esp fehlt, eax ist an anderer stelle. Um push ds/es musst du auch nicht herumbasteln. Das funktioniert und pusht ein 32 bit Wort, mit den oberen 16 Bits auf 0.
Dieser Text wird unter jedem Beitrag angezeigt.

ehenkes

  • Gast
Gespeichert
« Antwort #53 am: 11. May 2009, 19:59 »
Zitat
Dein irq_common_stub passt übrigens nicht zu deinem struct regs.
Nein, das war nicht meines, sondern das "übersetzte" von tyndur. Ich hatte darum gebeten zu prüfen, ob ich von AT&T sauber nach Intel übersetzt habe.
Meines sieht so aus:
extern _irq_handler

irq_common_stub:
    ;pusha
    push eax
    push ecx
    push edx
    push ebx
    push esp
    push ebp
    push esi
    push edi
   
    push ds
    push es
    push fs
    push gs

    mov ax, 0x10
    mov ds, ax
    mov es, ax
    mov fs, ax
    mov gs, ax
   
    mov eax, esp
    push eax
    mov eax, _irq_handler
    call eax
    pop eax

    pop gs
    pop fs
    pop es
    pop ds
   
   ;popa
    pop edi
    pop esi
    pop ebp
    pop esp
    pop ebx
    pop edx
    pop ecx
    pop eax

    add esp, 8
    iret
Das passt doch zu
typedef struct regs
{
    ULONG gs, fs, es, ds;
    ULONG edi, esi, ebp, esp, ebx, edx, ecx, eax;
    ULONG int_no, err_code;
    ULONG eip, cs, eflags, useresp, ss;
}registers_t;

Wenn allerdings ein regs log bei einer exception stattfindet, stimmen die Werte nicht zusammen, keine Ahnung warum.  :?
« Letzte Änderung: 11. May 2009, 20:30 von ehenkes »

ehenkes

  • Gast
Gespeichert
« Antwort #54 am: 11. May 2009, 20:01 »
Um push ds/es musst du auch nicht herumbasteln. Das funktioniert und pusht ein 32 bit Wort, mit den oberen 16 Bits auf 0.Das ist euer tyndur code.  :-o

ehenkes

  • Gast
Gespeichert
« Antwort #55 am: 11. May 2009, 20:26 »
Zitat
Allerdings eignet sich das nicht, um esp zu ändern, weil bei einem popa esp nicht geladen wird. Deswegen hat handle_int den Rückgabewert für esp.
Moment, das ist wichtig. Du meinst, wenn ich pusha/popa durchführe, ohne esp wie bei euch als ersten Parameter auf dem Stack an eine C-Funktion zu geben, hat eine esp-Änderung im irq_handler keine Wirkung, weil durch popa das alte esp wieder zurück geladen wird? Daher habt ihr doch auf push esp / pop esp komplett verzichtet und das selbst "außen" erledigt, habe ich das richtig verstanden?

Noch eine Frage zu meiner regs struct:
Ist das die Reihenfolge der gepushten register bis zum irq_handler oder hat dies seine eigene konstante Regel, so dass dies bei einem Interrupt immer gleich aussieht. ich stelle nämlich beim Loggen fest, dass Register vermischt sind. Kann natürlich auch vom falschen task_switch kommen.
Eure Struktur sieht ja genauso aus wie meine, sieht nach einer allgemeinen von Intel bestimmten Regel aus.

Ich habe mal selbst gewühlt und in James Molloy's Code folgende Kommentare gefunden:
    popa                     ; Pops edi,esi,ebp...
    add esp, 8     ; Cleans up the pushed error code and pushed ISR number
    sti
    iret           ; pops 5 things at once: CS, EIP, EFLAGS, SS, and ESP
Demnach ist es eine Mischung aus den selbst gepushten Registern und dem vom Interrupt-Mechanismus gepushten Daten, richtig? Das letzte ESP ist das useresp.
Warum er vor dem iret ein sti setzt, ist mir nicht klar.
« Letzte Änderung: 11. May 2009, 21:36 von ehenkes »

ehenkes

  • Gast
Gespeichert
« Antwort #56 am: 11. May 2009, 22:14 »
Wenn ich eure Technik verwende, kann ich dann nach dem ersten Parameter esp die regs zusätzlich abfangen?
ULONG irq_handler1(ULONG esp, struct regs* r){...} :mrgreen:
« Letzte Änderung: 11. May 2009, 22:17 von ehenkes »

ehenkes

  • Gast
Gespeichert
« Antwort #57 am: 11. May 2009, 22:22 »
Zitat
Und der "eigentliche Taskswitch" ist dann das mov %eax, %esp im allgemeinen Interrupthandler-Code.
... und was ist mit eip?

Jidder

  • Administrator
  • Beiträge: 1 625
    • Profil anzeigen
Gespeichert
« Antwort #58 am: 11. May 2009, 22:30 »
Nein, das war nicht meines, sondern das "übersetzte" von tyndur.
Hm, aus kernel2... mit dem hab ich nichts zu tun, deswegen hab ich das nicht erkannt..

Ich hatte darum gebeten zu prüfen, ob ich von AT&T sauber nach Intel übersetzt habe. 
Meines sieht so aus:
[...]
Sorry, hab ich übersehen. Sieht aber in Ordnung aus.

Wenn allerdings ein regs log bei einer exception stattfindet, stimmen die Werte nicht zusammen, keine Ahnung warum.  :?
Du musst beachten, dass manche Exceptions einen Fehlercode auf den Stack legen, und manche nicht. (Und IRQs auch nicht.) Bei Exceptions ohne Fehlercode und IRQs solltest du direkt am Anfang 0 auf den Stack legen. Irgendwie so sollte das bei dir aussehen:

; Beispielsweise legt Exception 0 (Division durch 0) keinen Fehlercode
; auf den Stack, deswegen machen wir das.
exception_stub_0:
push dword 0 ; Ersatz für Fehlercode
push dword 0 ; Nummer des Interrupts
jmp irq_common_stub
...
; Bei Exception 8 legt die CPU selbst einen Fehlercode auf den Stack, also machen wir das *nicht*
exception_stub_8:
push dword 8 ; Nummer des Interrupts
jmp irq_common_stub
...
; IRQs legen keinen Fehlercode auf den Stack, deswegen machen wir das.
irq_stub_0:
push dword 0 ; Ersatz für Fehlercode
push dword 32 ; Nummer des Interrupts (IRQ 0 ist z.B. nach Interrupt 32 gemappt)
jmp irq_common_stub
...
Diese kurzen 2- bis 3-zeiligen Stubs springen zum "größeren" Stub, der dann die ganzen Register sichert, und zur C-Routine springt. Die hab ich mal "irq_common_stub" genannt, weil die aus deinem vorherigen Post (vermutlich) genau passen sollte (wichtig ist das add esp, 8, das die pushs wieder rückgängig macht).
Ich hoffe das ist so einigermaßen verständlich.

Zitat
Allerdings eignet sich das nicht, um esp zu ändern, weil bei einem popa esp nicht geladen wird. Deswegen hat handle_int den Rückgabewert für esp.
Moment, das ist wichtig. Du meinst, wenn ich pusha/popa durchführe, ohne esp wie bei euch als ersten Parameter auf dem Stack an eine C-Funktion zu geben, hat eine esp-Änderung im irq_handler keine Wirkung, weil durch popa das alte esp wieder zurück geladen wird?
Nein, der Grund ein anderer. PUSHA legt zwar die Werte der 8 Register inkl. ESP auf den Stack, aber POPA überspringt ESP einfach beim auslesen. In den Intel Manuals (Volume 2 - Instruction Set Reference) ist das so beschrieben:
POPA:
    EDI <- Pop();
    ESI <- Pop();
    EBP <- Pop();
    Increment ESP by 4; (* Skip next 4 bytes of stack *)
    EBX <- Pop();
    EDX <- Pop();
    ECX <- Pop();
    EAX <- Pop();
POPA lädt ESP also gar nicht. Das liegt zwischen EBP und EBX, aber wird einfach übersprungen.

Ich weiß nicht mehr, ob ich das war, der die Interrupt Handler (in kernel(1)) geschrieben hat, aber wenn ich es gewesen wäre, dann hätte ich kein POP ESP verwendet, weil mir das zu unübersichtlich geworden wäre. Einfach direkt den Stack wechseln mittels des Rückgabewertes an einer Stelle, die wenig Probleme verursacht, ist hingegen recht einfach.

Direkt nach dem Aufruf der C-Routine ist halt nichts besonderes auf dem Stack, und dies erleichert es später für die Erstellung von neuen Tasks den Stack "aus dem Nichts" aufzubauen. Der Code dafür in tyndur ist natürlich mit der Zeit gewachsen, dennoch spiegelt er imo gut den Aufbau des Stacks wider. Vor allem muss er nicht mit irgendwelchen Tricks arbeiten um zu berücksichtigen, dass in der Mitte von irgendwas (C-Code, POP-Serie, ...) der Stack gewechselt wird.


Noch eine Frage zu meiner regs struct:
Ist das die Reihenfolge der gepushten register bis zum irq_handler oder hat dies seine eigene konstante Regel
Teils, teils. CS, EIP, EFLAGS sind immer dabei. SS und ESP nur, wenn der Interrupt nicht im Kernel Mode (also Ring 1-3) auftrat. (Jep, die Beiden sind die Werte aus dem User Mode.) Außerdem legt die CPU selbst manchmal den Fehlercode auf den Stack. Auf diese 3 bis 6 Werte und deren Anordnung hast du keinen direkten Einfluss.

Die anderen Register (General Purpose Register, Segment Register), kannst du in beliebiger Reihenfolge nach den anderen Registern auf den Stack sichern (oder auch nicht).

Der Aufbau der Stackframes ergibt sich wohl einfach daraus, dass sich eine Art natürliche Ordnung ergibt: Zuerst werden die "flüchtigsten" Register (CS:EIP, SS:ESP) gesichert, dann die "weniger flüchtigen" (EAX, ...), dann erst die Segmentregister, die sich sehr selten ändern.

Zitat
Warum er vor dem iret ein sti setzt, ist mir nicht klar.
Mir ebenfalls nicht. Interrupts und deren Sperrung im Kernel sind eine Geschichte für sich ...

Ich komm ja kaum hinterher ...

Wenn ich eure Technik verwende, kann ich dann nach dem ersten Parameter esp die regs zusätzlich abfangen?
ULONG irq_handler1(ULONG esp, struct regs* r){...}
Viel besser: esp ist gleich r, denn der Parameter esp zeigt auf den Stack, genauer gesagt auf die ganzen Register. Der C-Handler sieht in tyndur im Prinzip so aus:
unsigned int handle_int(unsigned int esp)
{
    struct regs * regs = *((struct regs **)esp);
    // und hier kannst du mit regs weiter arbeiten.
}

Zitat
Und der "eigentliche Taskswitch" ist dann das mov %eax, %esp im allgemeinen Interrupthandler-Code.
... und was ist mit eip?
Das liegt auf dem Stack.
« Letzte Änderung: 11. May 2009, 22:33 von PorkChicken »
Dieser Text wird unter jedem Beitrag angezeigt.

ehenkes

  • Gast
Gespeichert
« Antwort #59 am: 11. May 2009, 23:10 »
@PorkChicken: danke für die Infos. Du hast ein Superwissen. Das mit popa wusste ich bisher nicht.

Ich habe meinen Code versuchsweise umgebaut, lande aber noch bei einem GPF, war vielleicht ein Bisschen zu viel auf einmal.

Ich poste es mal der Reihe nach, vielleicht seht ihr den Fehler:

asm stub:
irq_common_stub:
    push eax
push ecx
push edx
push ebx
push ebp
push esi
push edi

push ds
    push es
    push fs
    push gs

    mov ax, 0x10
    mov ds, ax
    mov es, ax
    mov fs, ax
    mov gs, ax

    push esp              ; parameter of _irq_handler1
    call _irq_handler1    ; 
    mov esp, eax          ; return value: changed or unchanged esp

    pop gs
    pop fs
    pop es
    pop ds
   
pop edi
pop esi
pop ebp
pop ebx
pop edx
pop ecx
pop eax

add esp, 8
iret

versuchsweise neuer irq_handler:
ULONG irq_handler1(ULONG esp)
{
    ULONG retVal;
    struct regs* r = *((struct regs **)esp);

    if(pODA->ts_flag==0)
    {
       retVal = esp;
    }
    else
    {
       // task_switch1:
       retVal = task_switch1(esp);
    }

    /* This is a blank function pointer */
    void (*handler)(struct regs* r);

    /* Find out if we have a custom handler to run for this IRQ, and then finally, run it */
    handler = irq_routines[r->int_no - 32];
    if (handler) { handler(r); }

    /* If the IDT entry that was invoked was greater than 40 (IRQ8 - 15),
    *  then we need to send an EOI to the slave controller */
    if (r->int_no >= 40) { outportb(0xA0, 0x20); }

    /* In either case, we need to send an EOI to the master interrupt controller too */
    outportb(0x20, 0x20);

    return retVal;
}

task_switch1:
ULONG task_switch1(ULONG esp)
{
    // If we haven't initialised tasking yet, just return.
    if(!current_task) return esp;

    current_task->esp = esp;   // save esp

    //old_task ==> new_task
    current_task = current_task->next;
    if(!current_task) current_task = ready_queue;

    // new_task
    current_directory = current_task->page_directory;
    set_kernel_stack(current_task->kernel_stack+KERNEL_STACK_SIZE);
    tss_entry.cr3 = current_directory->physicalAddr;

    return current_task->esp;  // return esp
}

Ich habe eine task struct:
typedef struct task
{
    int id;                           // Process ID.
    ULONG esp, ebp;                   // Stack and base pointers.
    ULONG eip;                        // Instruction pointer.
    page_directory_t* page_directory; // Page directory.
    ULONG kernel_stack;               // Kernel stack location.
    struct task* next;                // The next task in a linked list.
} task_t;


die tss_entry bildet ein TSS nach. Ich benutze nur ein einziges. Da blicke ich aber noch nicht durch, was ich selbst setzen muss und was die CPU setzt.

Super Forum hier. Ein dickes Lob!  :-)
Wenn ihr mir helft, diesen Multitasking - Kram zum Laufen zu bringen, werde ich hier Stammgast und schreibe euch das wiki voll.

Fehler mit pODA->ts_flag == 0 :
Zitat
Welcome to PrettyOS 0.07
initial_esp: 001FFED8h
GDT, IDT, ISRS, IRQ, timer, keyboard install
paging install

< bricht vor dem tasking install ab, sti ist gesetzt >

< Das kommt beim regs->log heraus: >
err_code: 00000000h address(eip): 00000001h edi: 000B8FDCh esi: 001FFE68h ebp: 001FFE5Ch eax: FFFFFFFFh ebx: 00000010h ecx:
00000064h edx: 001FFE68h cs: 8 ds: 16 es: 16 fs: 16 gs 16 ss 40361
int_no 13 eflags 00010016h useresp 00000010h
General Protection Fault >>> Exception. System Halted! <<<



« Letzte Änderung: 11. May 2009, 23:24 von ehenkes »

 

Einloggen