Lowlevel

Lowlevel => OS-Design => Thema gestartet von: TheThing am 19. March 2009, 12:15

Titel: Software Multitasking
Beitrag von: TheThing am 19. March 2009, 12:15
Hi,
hab gestern versucht (Hardware-)Multitasking in meinen Kernel einzubauen, was in einem GPF geendet hat. Dann hab ich gelesen, das im Long-Mode Hardware-Multitasking sowieso nicht mehr geht.
Mein Problem ist, das ich KEINE Ahnung hab, wie man software-multitasking implementiert.
Software-Multitasking funktioniert (wenn ich das richtig verstanden habe) so:
Task1 wird von Timer unterbrochen
Kernel pusht Daten (Register usw.) auf den Stack von Task1
Kernel läd Stack von Task2
Kernel holt Daten vom Stack

So, wie springt jetzt aber der Kernel zurück zum Task?
Habt ihr da Tutorials, code oder so was für mich?
Titel: Re: Software Multitasking
Beitrag von: rizor am 19. March 2009, 13:15
Das machst du dann durch iret.
Du unterbrichst den Prozess durch einen IRQ und springst damit durch iret zurück
Titel: Re: Software Multitasking
Beitrag von: blitzmaster am 19. March 2009, 16:45
Du musst darauf achten, dass du beim Laden der Daten vom Stack des 2. Tasks zuletzt die Rücksprungadresse (+ segment glaub ich) auf den Stack legst, damit iret funktioniert
Titel: Re: Software Multitasking
Beitrag von: TheThing am 20. March 2009, 12:43
schonmal danke für die Antworten, aber ich hab noch en paar Fragen:
Was mache ich wenn der Task das erste Mal gestartet werden soll?
Könnt ihr mir evtl. sagen wo ich Beispielcode finde?
Titel: Re: Software Multitasking
Beitrag von: rizor am 20. March 2009, 18:00
Da gibt es mehrere Möglichkeiten.
Das einfachste wäre, wenn du elf-Dateien benutzt.
Am besten du schaust dir im Coding-Bereich meinen Thread an.
Da habe ich die selben Fragen gehabt, da ich auch gerade Multitasking einbaue.
Titel: Re: Software Multitasking
Beitrag von: kevin am 21. March 2009, 11:10
ELF hat damit erstmal nichts zu tun, das ist ja nur ein Binärformat, in dem man Programme ablegen kann. Ein Task kann auch ein Kernelthread sein, also letztlich einfach eine Funktion im Kernel. Genau so wird Multitasking ja auch meistens zuerst getestet: Zwei Kernelthreads geben jeweils 'a' oder 'b' aus und in der Ausgabe sollte dann ein Wechsel sichtbar sein.

Was hier beim Multitasking an sich beim ersten Start gilt, ist, daß man den Stack genau so vorbereitet, wie er aussehen würde, wenn der laufende Task unterbrochen worden wäre. Statt den dann gesicherten Werten gibt man beim ersten Start eben den Anfangszustand rein.

Quellcode gibt es wie immer im tyndur-Repository (http://git.tyndur.org/?p=tyndur.git;a=blob;f=src/kernel2/src/tasks/thread.c;h=d869595c016c55abaa71e7ad659a9b9c8cddec72;hb=HEAD). ;)
Titel: Re: Software Multitasking
Beitrag von: TheThing am 23. March 2009, 16:17
danke  :-)
hm, C <ironie>meine Lieblingssprache</ironie>
ich glaub das ich das irgendwie hinkrieg ;)
Titel: Re: Software Multitasking
Beitrag von: ehenkes am 26. April 2009, 19:11
Ich habe die Funktion task switch (siehe Kap. 9, James Molloy) eingebaut. Klappt auch gut, wenn ich an der richtigen Stelle im Code umschalte. Erledige ich das via System Timer Handler, also an rein zufälliger Stelle, so erhalte ich einen General  Protection Fault. Wie muss der Scheduler arbeiten, um dies zu vermeiden? Gibt es da ein Vorbild?
Titel: Re: Software Multitasking
Beitrag von: kevin am 26. April 2009, 19:47
Dann machst du bei der Interruptverarbeitung was falsch. Der Userspace-Prozeß sollte an jeder beliebigen Stelle unterbrochen werden können. Wenn du eine bestimmte Stelle zum Umschalten brauchst, deutet das darauf hin, daß du den Zustand des Prozesses in deinem Interrupthandler nicht richtig oder nicht vollständig speicherst.

Du kannst dir mal den Interrupthandler-Stub (http://git.tyndur.org/?p=tyndur.git;a=blob;f=src/kernel2/src/arch/i386/interrupts/int_stubs.S;h=2672f3ea8bac614ae86ffae2f4043b0444a24259;hb=HEAD) von tyndur anschauen. Der sichert den gesamten Zustand auf den Stack und ruft anschließend den eigentlichen Interrupthandler in C auf.
Titel: Re: Software Multitasking
Beitrag von: ehenkes am 26. April 2009, 20:07
Danke für den Hinweis. So sieht der task_switch aus:
void task_switch()
{
    if (!current_task) return;
    ULONG esp, ebp, eip;
    asm volatile("mov %%esp, %0" : "=r"(esp));
    asm volatile("mov %%ebp, %0" : "=r"(ebp));
    eip = read_eip();
    if (eip == 0x12345) return;
    current_task->eip = eip;
    current_task->esp = esp;
    current_task->ebp = ebp;
    current_task = current_task->next;
    if (!current_task) current_task = ready_queue;
    eip = current_task->eip;
    esp = current_task->esp;
    ebp = current_task->ebp;
    current_directory = current_task->page_directory;
    set_kernel_stack(current_task->kernel_stack+KERNEL_STACK_SIZE);
    asm volatile("         \
      cli;                 \
      mov %0, %%ecx;       \
      mov %1, %%esp;       \
      mov %2, %%ebp;       \
      mov %3, %%cr3;       \
      mov $0x12345, %%eax; \
      sti;                 \
      jmp *%%ecx           "
                 : : "r"(eip), "r"(esp), "r"(ebp), "r"(current_directory->physicalAddr));
}
Titel: Re: Software Multitasking
Beitrag von: kevin am 26. April 2009, 20:21
Den eigentlichen Taskswitch solltest du nicht in C machen, Assembler ist besser geeignet. Der richtige Ort dafür sind die Interrupthandler. Der Interrupthandler speichert den alten Zustand auf den Stack (bevor C-Funktionen die Register verändern), ruft Kernelcode für das Interrupthandling auf (der dann möglicherweise den aktuellen Task ändert), stellt den Zustand des jetzt aktuellen Tasks wieder her und springt zurück. Jeder Task hat seinen eigenen Kernelstack, so daß der Zustand immer auf diesen Stack liegen bleibt und erst wieder abgebaut und geladen ist, wenn tatsächlich in diesen Task gesprungen werden soll.

Hast du dir den verlinkten tyndur-Code angeschaut? Der macht genau das.

Edit: Was man bei deinem Code noch erwähnen sollte, ist vielleicht, daß der Zustand eines Tasks aus mehr als eip und esp besteht. Da gehören wirklich alle Register dazu. Wenn die nicht gesichert werden, passieren interessante Dinge, aber sicher nicht, was man möchte.
Titel: Re: Software Multitasking
Beitrag von: ehenkes am 26. April 2009, 20:36
Zitat
Hast du dir den verlinkten tyndur-Code angeschaut? Der macht genau das.
Ja, das klingt überzeugend. Ich habe mich an James Molloy's Code gehalten, da mir C leichter fällt, leider hört er dort auf, wo die Probleme  anfangen. Daher kann ich nicht beurteilen, inwieweit sein Code wirklich brauchbar ist. Wie gesagt bisher GPF im Timer Handler. Werde mir Tyndur nun mal genauer anschauen.

Zitat
Du kannst dir mal den Interrupthandler-Stub von tyndur anschauen. Der sichert den gesamten Zustand auf den Stack und ruft anschließend den eigentlichen Interrupthandler in C auf.
Ich habe auch einen Interrupthandler in asm, der C ISR aufruft. Einen Software Interrupt habe ich bisher nur für system calls eingesetzt. Welchen interrupt verwendet tyndur für das Schalten der Tasks, oder läuft das beim Timer Handler (IRQ0) mit? 
Titel: Re: Software Multitasking
Beitrag von: kevin am 26. April 2009, 20:45
Das läuft normal über den Timer. Oder der Task kann natürlich vorzeitig über einen Syscall abgeben.
Titel: Re: Software Multitasking
Beitrag von: ehenkes am 26. April 2009, 22:10
Ich habe den Code in task_switch mit putch('!');  gecheckt. Der Fehler liegt im hinteren Teil im asm-code. Dort tritt der GPF auf.
Titel: Re: Software Multitasking
Beitrag von: kevin am 26. April 2009, 22:28
Mir ist dieser ganze Ansatz sehr suspekt. Ob das ganze möglicherweise funktionieren kann, hängt wohl auch davon ab, wo task_switch() aufgerufen wird. Es sollte wohl direkt von einem Interrupthandler sein. Kannst du ein bißchen mehr Code herzeigen?
Titel: Re: Software Multitasking
Beitrag von: ehenkes am 26. April 2009, 22:56
void timer_handler(struct regs* r)
{
    ++timer_ticks;
    if (eticks)
        --eticks;

    //TEST
    static ULONG c = 0;
    ++c;
    if(c>200)
    {
      task_switch();
      settextcolor(getpid(),0);
      c=0;
    }
    //TEST
}
das ist der Code im timer handler, der bei IRQ0 angestoßen wird. Ändert man den Wert von c:
Manchmal gelingt ein task_switch, manchmal keiner. Mal wird mit GPF beendet, mal mit Absturz ohne Fehlermeldung. Also echt chaotisches Verhalten.
Titel: Re: Software Multitasking
Beitrag von: kevin am 26. April 2009, 23:25
Hm, gut, theoretisch könnte das tun. Voraussetzung ist, daß kein Register das asm() in task_switch() überlebt, sondern gcc alles auf den Stack packt. Du könntest das vielleicht erreichen, indem du die nötigen Register in die Clobber-Liste reinnimmt. Ich bin mir im Moment selbst nicht sicher, welche das alles sind, ebx dürfte eins davon sein.

Ich glaube aber wirklich, daß du dir einen Gefallen tust, wenn du es ähnlich wie in tyndur machst. Dort gibt es viel weniger Dinge, die man beim Sichern der Register beachten muß, weil kein C-Compiler reinspielt.
Titel: Re: Software Multitasking
Beitrag von: ehenkes am 26. April 2009, 23:53
Zitat
indem du die nötigen Register in die Clobber-Liste reinnimmt.
Clobber-Liste, noch nie gehört. Hier kann man ja richtig was lernen.  :-D
Ich habe das gefunden:
asm volatile ( Anweisungen : Outputs : Inputs : Clobbers)

Wenn ich das richtig sehe, ist die Clobbers-Liste z.Z. leer:
asm volatile(" ..." :  : "r"(eip), "r"(esp), "r"(ebp), "r"(current_directory->physicalAddr));
Wie muss das genau aussehen hinter einem dritten Doppelpunkt? kenne mich da leider gar nicht gut aus mit der Syntax.

Ich habe mal vor dem Assemblercode in task_switch bochs abgefangen mit:
    /// TEST
      cli();
      asm volatile("hlt;");
      for(;;);
    /// TEST

    asm volatile("         \
01368000000p[WGUI ] >>PANIC<< POWER button turned off.
01368000000i[CPU0 ] CPU is in protected mode (halted)
01368000000i[CPU0 ] CS.d_b = 32 bit
01368000000i[CPU0 ] SS.d_b = 32 bit
01368000000i[CPU0 ] EFER   = 0x00000000
01368000000i[CPU0 ] | RAX=000000000000008f  RBX=000000000000d502
01368000000i[CPU0 ] | RCX=00000000000b8000  RDX=00000000000003d5
01368000000i[CPU0 ] | RSP=000000000018ff1c  RBP=000000000018ff44
01368000000i[CPU0 ] | RSI=000000000018ffd8  RDI=000000000018fff0
01368000000i[CPU0 ] |  R8=0000000000000000   R9=0000000000000000
01368000000i[CPU0 ] | R10=0000000000000000  R11=0000000000000000
01368000000i[CPU0 ] | R12=0000000000000000  R13=0000000000000000
01368000000i[CPU0 ] | R14=0000000000000000  R15=0000000000000000
01368000000i[CPU0 ] | IOPL=0 id vip vif ac vm rf nt of df if tf sf zf af pf cf
01368000000i[CPU0 ] | SEG selector     base    limit G D
01368000000i[CPU0 ] | SEG sltr(index|ti|rpl)     base    limit G D
01368000000i[CPU0 ] |  CS:0008( 0001| 0|  0) 00000000 000fffff 1 1
01368000000i[CPU0 ] |  DS:0010( 0002| 0|  0) 00000000 000fffff 1 1
01368000000i[CPU0 ] |  SS:0010( 0002| 0|  0) 00000000 000fffff 1 1
01368000000i[CPU0 ] |  ES:0010( 0002| 0|  0) 00000000 000fffff 1 1
01368000000i[CPU0 ] |  FS:0010( 0002| 0|  0) 00000000 000fffff 1 1
01368000000i[CPU0 ] |  GS:0010( 0002| 0|  0) 00000000 000fffff 1 1
01368000000i[CPU0 ] |  MSR_FS_BASE:0000000000000000
01368000000i[CPU0 ] |  MSR_GS_BASE:0000000000000000
01368000000i[CPU0 ] | RIP=000000000000d3f9 (000000000000d3f9)
01368000000i[CPU0 ] | CR0=0xe0000011 CR1=0x0 CR2=0x0000000000000000
01368000000i[CPU0 ] | CR3=0x00306000 CR4=0x00000000
01368000000i[CPU0 ] >> add esp, 0x00000010 : 83C410
01368000000i[CMOS ] Last time is 1240781579 (Sun Apr 26 23:32:59 2009)
01368000000i[     ] restoring default signal behavior
01368000000i[CTRL ] quit_sim called with exit code 1
Das stand dann in bochsout.txt.

 
Titel: Re: Software Multitasking
Beitrag von: kevin am 27. April 2009, 00:05
Clobber-Liste, noch nie gehört. Hier kann man ja richtig was lernen.  :-D
Das ist Sinn und Zweck des Forums. ;)

Zitat
Ich habe das gefunden:
asm volatile ( Anweisungen : Outputs : Inputs : Clobbers)

Wenn ich das richtig sehe, ist die Clobbers-Liste z.Z. leer:
asm volatile(" ..." :  : "r"(eip), "r"(esp), "r"(ebp), "r"(current_directory->physicalAddr));
Wie muss das genau aussehen hinter einem dritten Doppelpunkt? kenne mich da leider gar nicht gut aus mit der Syntax.
Genau, und dahinter die Registernamen, die geclobbert werden. Also ungefähr so: asm("" : : : "ebx", "esi", "edi", "ebp");

Zitat
01368000000p[WGUI ] >>PANIC<< POWER button turned off.
Ich benutze ja eher qemu als bochs und kenne daher die typischen Meldungen nicht. Aber für mich klingt das nach ganz normalem Ausschalten der VM und nicht nach Fehlermeldung.
Titel: Re: Software Multitasking
Beitrag von: ehenkes am 27. April 2009, 00:17
Zitat
Aber für mich klingt das nach ganz normalem Ausschalten der VM und nicht nach Fehlermeldung.
Ja, genau. Das ist vor dem Fehler.

Zitat
can't find a register in class `GENERAL_REGS' while reloading `asm'
    asm volatile("         \
      cli;                 \
      mov %0, %%ecx;       \
      mov %1, %%esp;       \
      mov %2, %%ebp;       \
      mov %3, %%cr3;       \
      mov $0x12345, %%eax; \
      sti;                 \
      jmp *%%ecx           "
      : : "r"(eip), "r"(esp), "r"(ebp), "r"(current_directory->physicalAddr) : "ebx", "edx", "esi", "edi" );
Ich verwende gcc.exe (Version 3.1) und ld.exe (Version 2.13) wegen Linkens des aout-Formats von NASM.

Mit : "ebx", "edx" ); compiliert es, gibt aber wieder Absturz.

esi oder edi mag er nicht an dieser Stelle.  :-D

Titel: Re: Software Multitasking
Beitrag von: ehenkes am 27. April 2009, 01:47
Nun bekomme ich nur noch Page Faults. Offenbar erhält der EIP unter gewissen Umständen einen falschen Wert.

Erster Task Switch nach 20 sec (eingestellt im Time Handler):
Page Fault (page not present) at 0040FC20h - EIP: 0040FC20h

Sehr viele Task switches pro Sekunde:
Page Fault (page not present) at 0F000123h - EIP: 0F000123h

Aber immerhin. Offenbar hat die Clobber List den GPF gegen PF getauscht.  :-)

Funktion read_eip() ist in Assembler codiert (process.asm):

global _read_eip
_read_eip:
    pop eax               ; Get the return address
    jmp eax               ; Return. Can't use RET because return
                          ; address popped off the stack.
Vielleicht liegt hier der Hund begraben.

Ich habe diese gegen
global _read_eip
_read_eip:
    pop eax
    push eax
    ret
ausgetauscht. Sieht irgendwie besser aus, hilft aber nicht.  :-P
Titel: Re: Software Multitasking
Beitrag von: kevin am 27. April 2009, 21:53
Zitat
can't find a register in class `GENERAL_REGS' while reloading `asm'
Ich verwende gcc.exe (Version 3.1) und ld.exe (Version 2.13) wegen Linkens des aout-Formats von NASM.
Kann sein, daß er esi und edi schon benutzt, um deine Variablen zwischenzuspeichern. Durch das "g" ist das alles etwas unbestimmt, nichts genaues weiß man nicht. Du kannst jetzt entweder versuchen, deinen Kernel hinterher zu disassemblieren und dir mal genau anzuschauen, ob der Zustand komplett und korrekt gespeichert wird. Oder du wirfst dieses Modell über den Haufen und machst den Switch im Assembler-Stub wie ich dir seit einigen Posts vorschlage. ;)
Titel: Re: Software Multitasking
Beitrag von: ehenkes am 28. April 2009, 19:32
Ich bin didaktisch interessiert, daher muss ich erst verstehen, was überhaupt falsch läuft. Clobbering ebx und edx, das leider noch zum Page Fault führt, gibt im objdump einen Hinweis, warum man nicht auch edi und esi clobbern kann:
35f:    fa                       cli   
 360:    89 f1                    mov    %esi,%ecx
 362:    89 fc                    mov    %edi,%esp
 364:    89 cd                    mov    %ecx,%ebp
 366:    0f 22 d8                 mov    %eax,%cr3
 369:    b8 45 23 01 00           mov    $0x12345,%eax
 36e:    fb                       sti   
 36f:    ff e1                    jmp    *%ecx
Titel: Re: Software Multitasking
Beitrag von: Jidder am 28. April 2009, 20:39
Erster Task Switch nach 20 sec (eingestellt im Time Handler):
Page Fault (page not present) at 0040FC20h - EIP: 0040FC20h
Sollte denn diese Adresse gemappt sein?
Titel: Re: Software Multitasking
Beitrag von: ehenkes am 29. April 2009, 06:09
Nicht mehr genau dieses Programm:
002E0000h  11111111111111111111111111111111
00300000h  11111111111111111111111111111111
00320000h  11111111111111111111111111111111
00340000h  11111111111111111111111111111111
00360000h  11111111111111111111111111111111
00380000h  11111111111111111111111111111111
003A0000h  11111111111111111111111111111111
003C0000h  11111111111111111111111111111111
003E0000h  11111111111111111111111111111111
00400000h  11111111111111111111111111111111
00420000h  11111111100000000000000000000000
00440000h  00000000000000000000000000000000
00460000h  00000000000000000000000000000000
00480000h  00000000000000000000000000000000
004A0000h  00000000000000000000000000000000
004C0000h  00000000000000000000000000000000
004E0000h  00000000000000000000000000000000
Ich denke ja.  :?

Im Timer Handler komme ich noch nicht klar damit. Um zu verstehen, was wirklich wichtig ist, habe ich task_switch() an eine Stelle in einer while-Schleife im Hauptprogramm verlagert, in der es "stabil" nach jedem sechsten Tastenanschlag umschaltet (ich benutze den PID als Farbgeber).

Ohne Clobbern geht das. Sobald ich aber ebx und edx clobbere, gibt es einen PF:
Zitat
task_switch before asm: eip: 0000D4AFh esp: 0018FFD8h ebp: 0018FFF0h cur_dir->phys: 0041E000h
Page Fault (page not present) at 0040FCA0h - EIP: 0040FCA0h
Titel: Re: Software Multitasking
Beitrag von: ehenkes am 29. April 2009, 06:17
clobbering ebx and edx ==> Page Fault:
      asm volatile("         \
      cli;                 \
      mov %0, %%ecx;       \
      mov %1, %%esp;       \
      mov %2, %%ebp;       \
      mov %3, %%cr3;       \
      mov $0x12345, %%eax; \
      sti;                 \
      jmp %%ecx;           "
      : : "r"(eip), "r"(esp), "r"(ebp), "r"(current_directory->physicalAddr)
      : "ebx","edx" );
   

objdump task.o -D > task.txt
35f:    fa                       cli   
 360:    89 f1                    mov    %esi,%ecx
 362:    89 fc                    mov    %edi,%esp
 364:    89 cd                    mov    %ecx,%ebp
 366:    0f 22 d8                 mov    %eax,%cr3
 369:    b8 45 23 01 00           mov    $0x12345,%eax
 36e:    fb                       sti   
 36f:    ff e1                    jmp    *%ecx
Man sieht hier, warum man edi und esi nicht clobbern kann.

Leere clobber list:   
351: fa cli
352: 89 d9 mov %ebx,%ecx
354: 89 f4 mov %esi,%esp
356: 89 fd mov %edi,%ebp
358: 0f 22 d8 mov %eax,%cr3
35b: b8 45 23 01 00 mov $0x12345,%eax
360: fb sti
361: ff e1 jmp *%ecx

clobbert man ebx:
35f:    fa              cli   
 360:    89 f1                  mov    %esi,%ecx
 362:    89 fc                  mov    %edi,%esp
 364:    89 d5                 mov    %edx,%ebp
 366:    0f 22 d8             mov    %eax,%cr3
 369:    b8 45 23 01 00  mov    $0x12345,%eax
 36e:    fb                       sti   
 36f:    ff e1                    jmp    *%ecx

clobbering edx:

 351:    fa                       cli   
 352:    89 d9                 mov    %ebx,%ecx
 354:    89 f4                  mov    %esi,%esp
 356:    89 fd                  mov    %edi,%ebp
 358:    0f 22 d8             mov    %eax,%cr3
 35b:    b8 45 23 01 00  mov    $0x12345,%eax
 360:    fb                       sti   
 361:    ff e1                    jmp    *%ecx
Titel: Re: Software Multitasking
Beitrag von: Jidder am 29. April 2009, 11:53
35f:    fa                       cli   
 360:    89 f1                    mov    %esi,%ecx
 362:    89 fc                    mov    %edi,%esp
 364:    89 cd                    mov    %ecx,%ebp
 366:    0f 22 d8                 mov    %eax,%cr3
 369:    b8 45 23 01 00           mov    $0x12345,%eax
 36e:    fb                       sti   
 36f:    ff e1                    jmp    *%ecx
Hier ist das Register %ecx doppelt belegt. Am Anfang ist da als Eingabe der Basepointer drin. Dann wird %ecx mit %esi (dem Page Directory) überschrieben. Zwei Zeilen später wird %ecx nach %ebp kopiert. %ebp enthält jetzt also das Page Directory, und nicht den Stack Pointer. Du solltest auf benannte Register umsteigen:
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) );
Ist natürlich jetzt die Frage, ob das was bringt, oder ob dein ursprünglicher Code nicht genau das gleiche tut.

Außerdem gehören Register natürlich nicht auf die Clobber-List, wenn du sie (im Assembler Code) gar nicht benutzt. Wenn GCC die Register benutzt, weiß er schon selbst, was er tut. (Naja gut bei deinem alten GCC weiß man das nicht^^) Also ist nur eine leere Clobber-List korrekt.
Titel: Re: Software Multitasking
Beitrag von: kevin am 29. April 2009, 19:14
Außerdem gehören Register natürlich nicht auf die Clobber-List, wenn du sie (im Assembler Code) gar nicht benutzt. Wenn GCC die Register benutzt, weiß er schon selbst, was er tut. (Naja gut bei deinem alten GCC weiß man das nicht^^) Also ist nur eine leere Clobber-List korrekt.
Er benutzt die Register aber implizit. gcc weiß nicht, daß hier ein Taskwechsel stattfindet und könnte noch Zeug aus dem alten Task in den Registern stehen haben. Wenn man es unbedingt in C machen will, muß man also dafür sorgen, daß gcc alle Register auf den Stack packt und die Register das asm() nicht überleben. Dazu kann man die Clobber-Liste nehmen.
Titel: Re: Software Multitasking
Beitrag von: ehenkes am 01. May 2009, 08:30
Das Problem liegt leider auch noch an anderer Stelle. SS ist nicht korrekt. Momentan schlagen alle Schwächen gleichzeitig zu.  JM's Code ist im TaskSwitch Bereich nicht besonders brauchbar. Das wird mir immer klarer.
Titel: Re: Software Multitasking
Beitrag von: kevin am 01. May 2009, 09:41
Wieso sollte der Taskswitch-Code ss ändern müssen? Er ist doch vorher und nachher im Kernelcode?
Titel: Re: Software Multitasking
Beitrag von: ehenkes am 01. May 2009, 13:14
Ja, völlig richtig. Das Problem liegt tiefer, nämlich im Interrupt asm und C Code. JM's Code ist da nur in Ansätzen - sprich als erste Demo - hilfreich, aber in der Praxis versagt und verwirrt er konkret, wie bei mir geschehen.  Daher mache ich bezüglich des Task Switch nun einen kompletten Roll-back.

Mal noch zwei konkrete Fragen zu diesem Thema:
1) Sollte man auch die Flags/EFlags sichern? Ist das überhaupt notwendig?
2) Im GCC 3.1 geht in asm(...) nur pushf/popf, pushfd/popfd wird nicht erkannt (das könnte man natürlich nach NASM auslagern und zurück linken). Liegt das an dem Alter dieser GCC-Version (2002, ich verwende diese wegen aout, weil coff keinen gemischten 16/32-bit-code akzeptiert)? Die 386 Instruktionen sind doch schon uralt dagegen.
Titel: Re: Software Multitasking
Beitrag von: bluecode am 01. May 2009, 13:24
1) Sollte man auch die Flags/EFlags sichern? Ist das überhaupt notwendig?
Ja, die werden aber automatisch bei einem Interrupt aus den Stack gelegt.

Zitat
2) Im GCC 3.1 geht in asm(...) nur pushf/popf, pushfd/popfd wird nicht erkannt (das könnte man natürlich nach NASM auslagern und zurück linken). Liegt das an dem Alter dieser GCC-Version (2002, ich verwende diese wegen aout, weil coff keinen gemischten 16/32-bit-code akzeptiert)? Die 386 Instruktionen sind doch schon uralt dagegen.
Warum brauchst du 16bit Instruktionen überhaupt? :-o
Abgesehen davon ist das offensichtlich keine dauerhaft Lösung...
Titel: Re: Software Multitasking
Beitrag von: ehenkes am 03. May 2009, 11:48
Zitat
Warum brauchst du 16bit Instruktionen überhaupt?

http://www.henkessoft.de/OS_Dev/OS_Dev1.htm#mozTocId412221
Dort finden sich [Bits 16] und [Bits 32] in einer Übersetzungseinheit (Übergang von RM nach PM). Vielleicht kann man das so verändern, so dass man COFF verwenden kann?

Titel: Re: Software Multitasking
Beitrag von: Jidder am 03. May 2009, 11:57
Ich weiß nicht, warum das in vielen Tutorials immer so kompliziert gemacht wird, und 16 und 32 Bit Code vermischt wird ... Man kann doch einfach den Kernel komplett in 32 Bit halten (und im Bootloader natürlich weiterhin nur 16 Bit benutzen). Am Ende des Bootloader muss man den Sprung in den Protected Mode dann mit dem Sprung in den Kernel kombinieren.
Titel: Re: Software Multitasking
Beitrag von: bluecode am 03. May 2009, 11:59
Im Binärformat kann man auf jeden Fall den Code vermischen. Aber ob dir das reicht weiß ich nicht. Müsste man halt eventuell noch ein Feld am Anfang unterbringen, in dem die Adresse vom Entry-Point steht oder so.

Ansonsten hat Jidder natürlich Recht, aber das hab ich ja oben auch mehr oder weniger gesagt...
Titel: Re: Software Multitasking
Beitrag von: ehenkes am 03. May 2009, 12:11
In diesem Bild sieht man die Zusammenhänge:
http://www.henkessoft.de/OS_Dev/Bilder/make_process.PNG

Der Übergang von RM nach PM erfolgt in kernel.asm (16/32 bit).
Damit das Linken klappt, erzeuge ich aout-Format, was problemlos von der Kombination gcc 3.1 / ld 2.13 (in DJGPP) geschluckt wird.

Naja, vielleicht sollte mir das einfach egal sein. In anderen Tutorials habe ich bisher nur kompliziertere Wege gesehen als im oben genannten Tut.

Um GRUB führt auf Dauer sowieso kein Weg vorbei, werde ich in Teil 3 gezwungermaßen wohl angehen müssen. MS Windows User mögen dieses Teil allerdings überhaupt nicht, weil sie GRUB nicht haben. Eine selbst entwickelte Alternative fehlt. Unter Vista gibt es doch auch einen Bootloader? (verwende selbst noch Win XP)
Titel: Re: Software Multitasking
Beitrag von: ehenkes am 03. May 2009, 12:18
In diesem Bild sieht man die Zusammenhänge:
http://www.henkessoft.de/OS_Dev/Bilder/make_process.PNG

Der Übergang von RM nach PM erfolgt in kernel.asm (16/32 bit).
Damit das Linken klappt, erzeuge ich aout-Format, was problemlos von der Kombination gcc 3.1 / ld 2.13 (in DJGPP) geschluckt wird.

Naja, vielleicht sollte mir das einfach egal sein. In anderen Tutorials habe ich bisher nur kompliziertere Wege gesehen als im oben genannten Tut.

Ein Sprung aus dem Binär-Format nach C-Funktionen war nicht erlaubt. Daher konnte ich kernel.asm nicht als bin verwenden. ich musste auf o, dann war da aber das 16/32 bit Problem beim Linken, was ich via aout (es lebe hoch) lösen konnte.  :-)

Um GRUB führt auf Dauer sowieso kein Weg vorbei, werde ich in Teil 3 gezwungermaßen wohl angehen müssen. MS Windows User mögen dieses Teil allerdings überhaupt nicht, weil sie GRUB nicht haben. Eine selbst entwickelte Alternative fehlt. Unter Vista gibt es doch auch einen Bootloader? (verwende selbst noch Win XP)
Titel: Re: Software Multitasking
Beitrag von: bluecode am 03. May 2009, 12:45
Zitat
Ein Sprung aus dem Binär-Format nach C-Funktionen war nicht erlaubt. Daher konnte ich kernel.asm nicht als bin verwenden.
Jo das ist klar, ich dachte auch eher an das Ausgabeformat .bin für den ges. gelinkten Kernel, was du aber offensichtlich schon verwendest.

Zitat
ich musste auf o, dann war da aber das 16/32 bit Problem beim Linken, was ich via aout (es lebe hoch) lösen konnte.  :-)
Wie bereits gesagt, 16bit Code mit 32Bit C-Code mischen ist nicht optimal. Dafür habe ich auch keine gescheite Lösung gesehen/gefunden.

Zitat
Unter Vista gibt es doch auch einen Bootloader? (verwende selbst noch Win XP)
Kommt gut, wenn man sein OS mal weitergeben möchte. :wink: Außerdem bist du dann an das gebunden was dieser Bootloader an Ausgabeformaten unterstützt. Der ist sicher nicht so flexibel wie grub.
Titel: Re: Software Multitasking
Beitrag von: Jidder am 03. May 2009, 12:57
Unter Vista gibt es doch auch einen Bootloader? (verwende selbst noch Win XP)
Soweit ich weiß, ist der Bootloader ziemlich der gleiche seit NT. Der kann allerdings auch nur Windows laden, oder chain loading, also eine Datei als Bootsektor in den Speicher nach 0x00007c00 laden.
Titel: Re: Software Multitasking
Beitrag von: matheguru am 04. May 2009, 14:45
Falls du das so meinst, dass erst der Vistabootloader chain loaden kann, liegst du falsch den das macht auch der NTLDR von NT-Systemen. Einfach einen Eintrag in der Boot.ini Datei, dabei muss sich der bootsector in einer datei auf der C-Partition im Haubtverzeichniss befinden: z.B. C:\BOOTSECT.DOS, wenn die Datei BOOTSECT.DOS heist.
Titel: Re: Software Multitasking
Beitrag von: TheThing 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?
Titel: Re: Software Multitasking
Beitrag von: bluecode am 08. May 2009, 19:28
Hardware.
Titel: Re: Software Multitasking
Beitrag von: TheThing am 09. May 2009, 19:16
ok, und ab welcher Version nutzt Linux Software Multitasking?
Titel: Re: Software Multitasking
Beitrag von: bluecode 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.
Titel: Re: Software Multitasking
Beitrag von: ehenkes 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?



Titel: Re: Software Multitasking
Beitrag von: Jidder 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.
Titel: Re: Software Multitasking
Beitrag von: kevin am 11. May 2009, 18:36
Und der "eigentliche Taskswitch" ist dann das mov %eax, %esp im allgemeinen Interrupthandler-Code.
Titel: Re: Software Multitasking
Beitrag von: ehenkes 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. 


Titel: Re: Software Multitasking
Beitrag von: kevin 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.
Titel: Re: Software Multitasking
Beitrag von: ehenkes 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? 
Titel: Re: Software Multitasking
Beitrag von: kevin 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.
Titel: Re: Software Multitasking
Beitrag von: ehenkes 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.
Titel: Re: Software Multitasking
Beitrag von: Jidder am 11. May 2009, 19:48
ja, hat es. struct int_stack_frame (http://git.tyndur.org/?p=tyndur.git;a=blob;f=src/kernel/include/intr.h;h=f07bf6133421c2ba620a710e20316fd3aefe3b61;hb=master#l54). 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.
Titel: Re: Software Multitasking
Beitrag von: ehenkes 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.  :?
Titel: Re: Software Multitasking
Beitrag von: ehenkes 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
Titel: Re: Software Multitasking
Beitrag von: ehenkes 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.
Titel: Re: Software Multitasking
Beitrag von: ehenkes 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:
Titel: Re: Software Multitasking
Beitrag von: ehenkes 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?
Titel: Re: Software Multitasking
Beitrag von: Jidder 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 (http://git.tyndur.org/?p=tyndur.git;a=blob;f=src/kernel/src/task.c;h=a86abab9c527697e4447ac5570285e23e6e182e9;hb=master#l213) 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.
Titel: Re: Software Multitasking
Beitrag von: ehenkes 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! <<<



Titel: Re: Software Multitasking
Beitrag von: Jidder am 11. May 2009, 23:23
Hast du beachtet, dass du nicht einfach irq_common_stub in die IDT eintragen darfst, weil du da nicht den Fehlercode sowie die Interruptnummer auf den Stack legst, sondern du einen Stub ähnlich wie diesen verwenden musst? So einen Stub meine ich:
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
Sonst ist dein Stack nicht wie erwartet aufgebaut, und du bekommst Probleme beim Auslesen der Register, und spätestens beim IRET einen GPF.

Dass in der Parameter esp auch als Zeiger auf dein Register-Struct zuverwenden ist, hab ich vielleicht auch nicht ganz gut rübergebracht. Ich meinte das in etwa so:
ULONG irq_handler1(ULONG esp)
{
     struct regs* r = (struct regs*)esp;
     ...

Ansonsten hilft nur den Fehler einzugrenzen. Es hilft manchmal mittels Breakpoints an wichtigen Stellen sich die Register genau anzugucken, und zu überlegen, ob sie die erwarteten Werte enthalten. Und wenn ja, musst du überlegen, ob die Erwartung sinnvoll ist. Außerdem kannst du natürlich deinen Code kürzen, indem du Behandlung des PICs sowie das Paging rauswirfst.
Titel: Re: Software Multitasking
Beitrag von: Jidder am 11. May 2009, 23:26
Und IRQs (zumindest der Timer-IRQ) sollten auch funktionieren, wenn du sie direkt mit INT 32 (o.ä.) aufrufst. Falls du den PIC ignorierst, ist das ganz hilfreich.
Titel: Re: Software Multitasking
Beitrag von: kevin am 11. May 2009, 23:38
Das mit cr3 im TSS dürfte auch nicht funktionieren, oder? Wenn ich mich recht erinnere werden da für Software-Multitasking nur die Stack-Einträge (also esp und ss jeweils für Kernel- und Userspace) benutzt. Du solltest also stattdessen irgendwo gegen Ende des C-Interrupt-Handlers manuell cr3 neu laden (nur wenn es wirklich einen Taskwechsel gab, das ist teuer!)
Titel: Re: Software Multitasking
Beitrag von: ehenkes am 11. May 2009, 23:43
so sieht das im asm stub aus:
_irq0:
    cli
    push byte 0
    push byte 32
    jmp irq_common_stub
Titel: Re: Software Multitasking
Beitrag von: kevin am 11. May 2009, 23:50
Das cli kannst du dir sparen, wenn du den Gate-Typ richtig gesetzt hast (Interrupt Gate statt Trap Gate), das ist dann automatisch dabei. Ansonsten kommt mir ein byte-Push etwas klein vor - ich hätte sogar gedacht, daß man überhaupt nicht byteweise pushen kann. dword wie PorkChicken es geschrieben hat wäre wohl sinnvoller (und beim Fehlercode ist das auch, was die CPU pusht, wenn sie es selber macht).
Titel: Re: Software Multitasking
Beitrag von: ehenkes am 11. May 2009, 23:52
So ich habe jetzt mal user mode, vfs, RAM disk weg gelassen. Mit pODA->ts_flag ==0 kann ich mittels IRQ0 eine Zeitschleife realisieren, also sollte der IRQ-Mechanismus gehen. Das geht:
    k_clear_screen();
    settextcolor(getpid(),0);
    printformat("SS: %x, ESP: %x, EBP: %x, CS: %x, DS: %x\n", fetchSS(),fetchESP(),fetchEBP(),fetchCS(),fetchDS());

    sti();
    sleepSeconds(5);

    //pODA->ts_flag = 1;
    ULONG c=0;
    while(TRUE)
{
  if( k_checkKQ_and_print_char() )
  {
    ++c;
    if(c>5)
    {
      c=0;
      settextcolor(4,0);
      printformat("\nT: %x H: %x WRITE: %i Read: %i ", pODA->pTailKQ, pODA->pHeadKQ, pODA->KQ_count_write, pODA->KQ_count_read);
          printformat("*T: %c %i *H: %c %i\n", *(pODA->pTailKQ),*(pODA->pTailKQ),*(pODA->pHeadKQ),*(pODA->pHeadKQ));
          settextcolor(2,0);
          settextcolor(getpid(),0);
    }
  }
}
return 0;
Schon mal ein guter Anfang.
Titel: Re: Software Multitasking
Beitrag von: ehenkes am 11. May 2009, 23:55
Zitat
Das cli kannst du dir sparen, wenn du den Gate-Typ richtig gesetzt hast (Interrupt Gate statt Trap Gate), das ist dann automatisch dabei.
Ich verwende IDT.


Zitat
Ansonsten kommt mir ein byte-Push etwas klein vor - ich hätte sogar gedacht, daß man überhaupt nicht byteweise pushen kann. dword wie PorkChicken es geschrieben hat wäre wohl sinnvoller (und beim Fehlercode ist das auch, was die CPU pusht, wenn sie es selber macht).
Ich habe mir das aus Bran's und James Molloy's Tutorial abgeschaut. Ging aber die ganze Zeit, sollte also nicht das Problem sein.
Titel: Re: Software Multitasking
Beitrag von: ehenkes am 12. May 2009, 00:00
Mit gesetztem ts_flag gibt es leider einen GPF:
Zitat
jksdghfjkdghdfjkgh: 0018FFE0h, EBP: 0018FFF0h, CS: 00000008h, DS: 00000010h
T: 0000DAF6h H: 0000DAF6h WRITE: 18 Read: 18 *T:  0 *H:  0100

TSS log:
prev_tss: 00000000h esp0: 40105800h ss0: 00000010h esp1: 00000000h ss1: 00000000h esp2: 00000000h ss2: 00000000h cr3: 0041E000h eip: 00000000h eflags: 00000000h eax: 00000000h ecx: 00000000h edx: 00000000h ebx: 00000000h esp: 00000000h ebp: 00000000h esi: 00000000h edi: 00000000h es: 00000013h cs: 0000000Bh ss: 00000013h ds: 00000013h fs: 00000013h gs: 00000013h ldt: 00000000h trap: 00000000h iomap_base: 00000000h

err_code: 0000DAD0h address(eip): 00008497h
edi: 000B8FDCh esi: 000082ABh ebp: 00190000h eax: 0018FFD8h ebx: 00000000h ecx: 000B800Ah edx: 00000020h cs: 8 ds: 16 es: 16 fs: 16 gs 16 ss 753674 int_no 13 eflags 00010016h useresp 0000DAD0h
General Protection Fault >>> Exception. System Halted! <<<
Titel: Re: Software Multitasking
Beitrag von: ehenkes am 12. May 2009, 00:28
Ich poste mal den Code, da ich momentan nicht weiß, wo es am meisten hängt, werde aber dennoch Fehlereingrenzung versuchen. Vielleicht seht ihr das Problem mit einem Blick. Würde mich freuen, wenn ihr mir aus dem Tiefpunkt heraus helft. Vielleicht fehlt mir auch noch der Durchblick. Die Thematik ist komplex aber auch sehr interessant.  :-)

http://www.henkessoft.de/OS_Dev/Downloads/36i.zip

Leider hat mir euer Code aber auch nicht zum Multitasking geholfen.
Es ist echt verhext.

Falls ihr den Code compilieren wollt:
http://www.osdever.net/downloads/compilers/DJGPP-Installer-nocpp.exe

Ich verwende den Compiler gcc.exe (Version 3.1) und Linker ld.exe (Version 2.13) wegen des Formats aout für den Linker. Nur so konnte ich den Übergang 16/32 Bit schaffen, wollte aus didaktischen Gründen keinen GRUB bootloader (die Diskussion hatten wir schon, ihr habt mich überzeugt, bitte nicht nachsetzen).   

Titel: Re: Software Multitasking
Beitrag von: ehenkes am 12. May 2009, 00:56
Ich habe nun folgendes getestet: Umschalten von task A nach task A, um mal zu sehen, ob der ganze Mechanismus überhaupt läuft, und es geht!
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
    printformat("ts ");
    /*
    current_task = current_task->next;
    if(!current_task) current_task = ready_queue;
    */
    current_task = current_task; //same task TEST TEST TEST

    // new_task
    current_directory = current_task->page_directory;
    tss_entry.cr3 = current_directory->physicalAddr;

    set_kernel_stack(current_task->kernel_stack+KERNEL_STACK_SIZE);

    return current_task->esp;  // return esp
}
Zitat
ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts
 ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts t
s ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts
ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts
 ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts t
s ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts
ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts
 ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts t
s ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts
ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts
 ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts t
s ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts
ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts
 ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts t
s ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts
ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts
 ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts t
s ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts
ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts
 ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts t
s ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts
ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts
 ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts t
s ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts
ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts ts
:-)
Nun gilt es heraus zu finden, woran es liegt, dass das Umschalten von task A nach task B nicht fehlerfrei gelingt. Wie läuft das bei tyndur? Wie sieht da die task-Struktur aus?

Was muss genau beachtet werden, dass dies klappt?
    current_task = current_task->next;
    if(!current_task) current_task = ready_queue;

Titel: Re: Software Multitasking
Beitrag von: ehenkes am 12. May 2009, 01:19
Ich habe das jetzt so aufgesetzt, dass ich die Daten des zweiten Tasks vor dem GPF gerade noch sehe:
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
    printformat("ts: ");
    task_log(current_task);
    printformat("\n\n");

    current_task = current_task->next;
    if(!current_task) current_task = ready_queue;

    printformat("ts: ");
    task_log(current_task);
    printformat("\n\n");


    //current_task = current_task; //same task TEST TEST TEST

    // new_task
    current_directory = current_task->page_directory;
    tss_entry.cr3 = current_directory->physicalAddr;

    set_kernel_stack(current_task->kernel_stack+KERNEL_STACK_SIZE);

    return current_task->esp;  // return esp
}
Screen Output:
Zitat
SS: 00000010h, ESP: 0018FFE0h, EBP: 0018FFF0h, CS: 00000008h, DS: 00000010h

ts: id: 1 ebp: 00000000h esp: 0018FFC0h eip: 00000000h PD: 00000000h k_stack: 40101800h next: 40101814h

ts: id: 2 ebp: 0018FFF0h esp: 0018FFD8h eip: 0000D36Bh PD: 40102000h k_stack: 40105000h next: 40101844h

TSS log:
prev_tss: 00000000h esp0: 40105800h ss0: 00000010h esp1: 00000000h ss1: 00000000h esp2: 00000000h ss2: 00000000h cr3: 0041E000h eip: 00000000h eflags: 00000000h eax: 00000000h ecx: 00000000h edx: 00000000h ebx: 00000000h esp: 00000000h ebp: 00000000h esi: 00000000h edi: 00000000h es: 00000013h cs: 0000000Bh ss: 00000013h ds: 00000013h fs: 00000013h gs: 00000013h ldt: 00000000h trap: 00000000h iomap_base: 00000000h

err_code: 0000FFF8h address(eip): 00008497h edi: 000B8FDCh esi: 000082ABh ebp: 001FFFF8h eax: 0018FFD8h ebx: 00000000h ecx:
000B800Ah edx: 00000020h cs: 8 ds: 16 es: 16 fs: 16 gs 16 ss 0
int_no 13 eflags 00010002h useresp 001FFFF8h

General Protection Fault >>> Exception. System Halted! <<<
Titel: Re: Software Multitasking
Beitrag von: Jidder am 12. May 2009, 01:30
Ich hab deinen Code auch mal versucht zu debuggen. So ganz schlau, werd ich da gerade auch nicht. Bei mir tritt der GPF beim pop fs im Interrupt Handler auf. Seltsamerweise nicht beim pop gs davor. Außerdem tritt bei mir der Fehler nicht beim ersten Interrupt auf, sondern ein paar Interrupts später.

Irgendwas stimmt da mit dem Stack nicht (ein 0xd9a7 sollte da nicht einfach so draufliegen). Auszug aus dem Bochs Log:
Zitat
00158736424-e-@0000849d-[CPU0 ] fetch_raw_descriptor: GDT: index (d9a7)1b34 > limit (2f)

Ich denke du suchst das Problem an der falschen Stelle. Mehr kann ich dir um diese Zeit leider auch noch nicht sagen. Am besten verlässt du dich nicht nur auf deine eigenen Ausgaben, sondern nutzt den Debugger deines Emulators.
Titel: Re: Software Multitasking
Beitrag von: MNemo am 12. May 2009, 09:37
ich habe jetzt ein paar posts übersprungen:
aber mir ist aufgefallen das der parameter für _irq_handler1 nicht
vom stack geholt wird:
mov gs, ax

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

pop gs
Titel: Re: Software Multitasking
Beitrag von: ehenkes am 12. May 2009, 19:05
Zitat
add esp, 4
Ich habe dies hinzu gefügt, dann stürzt er noch früher mit GPF ab.

Zitat
Ich hab deinen Code auch mal versucht zu debuggen. So ganz schlau, werd ich da gerade auch nicht. Bei mir tritt der GPF beim pop fs im Interrupt Handler auf. Seltsamerweise nicht beim pop gs davor. Außerdem tritt bei mir der Fehler nicht beim ersten Interrupt auf, sondern ein paar Interrupts später.

Irgendwas stimmt da mit dem Stack nicht (ein 0xd9a7 sollte da nicht einfach so draufliegen). Auszug aus dem Bochs Log:
Zitat
00158736424-e-@0000849d-[CPU0 ] fetch_raw_descriptor: GDT: index (d9a7)1b34 > limit (2f)

Ich denke du suchst das Problem an der falschen Stelle. Mehr kann ich dir um diese Zeit leider auch noch nicht sagen. Am besten verlässt du dich nicht nur auf deine eigenen Ausgaben, sondern nutzt den Debugger deines Emulators.
Ich verwende Bochs. Wie wendest Du den Debugger genau an, damit Du diese Meldungen siehst? Mir fiel auf, dass beim Vergleich des eigenen error logs bei GPF und output von Bochs Unterschiede sind, und zwar bei eip, esi, ebp, eax, edx.

Titel: Re: Software Multitasking
Beitrag von: kevin am 12. May 2009, 19:20
ich habe jetzt ein paar posts übersprungen:
aber mir ist aufgefallen das der parameter für _irq_handler1 nicht
vom stack geholt wird:
mov gs, ax

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

pop gs
Nein, das wäre falsch. Die Funktion gibt das korrekte esp in eax zurück und das wird ja sofort gesetzt.
Titel: Re: Software Multitasking
Beitrag von: kevin am 12. May 2009, 19:35
Ich verwende Bochs. Wie wendest Du den Debugger genau an, damit Du diese Meldungen siehst?
Wenn meine bochs-Version nicht schon längst veraltet ist und sich alles verändert hat, brauchst du ungefähr sowas in deiner Konfigurationsdatei:
error: action=report
info: action=report
debug: action=report
Titel: Re: Software Multitasking
Beitrag von: ehenkes am 12. May 2009, 20:22
error: action=report
info: action=report
debug: action=ignore

steht da bei mir.

Nach dem GPF:
RAX=000000000000001a  RBX=0000000000000000
RCX=00000000000b8000  RDX=00000000000003d5
RSP=000000000018ff6c  RBP=000000000018ff94
RSI=000000000018ffa0  RDI=0000000000000004
IOPL=0 id vip vif ac vm rf nt of df if tf sf zf af PF cf
CS:0008( 0001| 0|  0) 00000000 000fffff 1 1
DS:0010( 0002| 0|  0) 00000000 000fffff 1 1
SS:0010( 0002| 0|  0) 00000000 000fffff 1 1
ES:0010( 0002| 0|  0) 00000000 000fffff 1 1
FS:0010( 0002| 0|  0) 00000000 000fffff 1 1
GS:0010( 0002| 0|  0) 00000000 000fffff 1 1
RIP=0000000000009a07 (0000000000009a07)
CR0=0xe0000011
CR1=0x0
CR2=0x0000000000000000
CR3=0x00306000
CR4=0x00000000
jmp .+0xfffffffe (0x00009a07) : EBFE

... habe nun auch die byte gegen dword getauscht, macht aber keinen Unterschied.  :-(
_irq0:
    cli
    push dword 0
    push dword 32
    jmp irq_common_stub

Ich habe mal nachgeschaut, was ein GPF im Rahmen des Switching bewirkt:

Zitat
Faults can occur in the task state segment (TSS) structure when:
  * switching to a busy task during a call or jump instruction
  * switching to an available task during an interrupt return (IRET) instruction
  * using a segment selector on a switch pointing to a TSS descriptor in the LDT

Hat jemand eine Idee, wo und wie ich genau ansetzen könnte, um das Problem zu lösen? Wie soll ich das schaffen, wenn selbst PorkChicken, der mich so hervorragend zur Umsetzung des tyndur-taskswitch-Mechanismus  geführt hat, den Fehler nicht leicht findet.  :?

Ich habe nochmals
von task A nach task A umgeschaltet:
Zitat
id: 1 esp: 0018FFB8h k_stack: 40101800h next: 40101814h
id: 1 esp: 0018FFB8h k_stack: 40101800h next: 40101814h
id: 1 esp: 0018FFBCh k_stack: 40101800h next: 40101814h
id: 1 esp: 0018FFBCh k_stack: 40101800h next: 40101814h
Kann das sein, dass esp dabei ab und zu um 4 Byte hin und her springt?

Was muss im task_switch im TSS eingetragen werden?
Titel: Re: Software Multitasking
Beitrag von: kevin am 12. May 2009, 22:25
error: action=report
info: action=report
debug: action=ignore

steht da bei mir.
Ja. ignore ist eben weniger Ausgabe als report.

Zitat
RIP=0000000000009a07 (0000000000009a07)
Ich weiß nicht, wohin du deinen Kernel legst (du benutzt ja kein GRUB), aber normal würde ich das als einen kaputten Wert ansehen.

Zitat
Ich habe mal nachgeschaut, was ein GPF im Rahmen des Switching bewirkt:

Zitat
Faults can occur in the task state segment (TSS) structure when:
  * switching to a busy task during a call or jump instruction
  * switching to an available task during an interrupt return (IRET) instruction
  * using a segment selector on a switch pointing to a TSS descriptor in the LDT
Du machst kein Hardware-Taskswitching, deswegen klingt das alles nicht so richtig relevant. Du kriegst deinen GPF entweder bei einem Interrupt oder beim Rücksprung aus einem Interrupt, würde ich sagen.

Zitat
Kann das sein, dass esp dabei ab und zu um 4 Byte hin und her springt?
Das klingt auf jeden Fall komisch. Ich würde mal versuchen rauszufinden, woran das liegt. Dann kannst du wahrscheinlich auch sagen, ob es harmlos oder böse ist.

Zitat
Was muss im task_switch im TSS eingetragen werden?
Nichts. Wie oben schonmal erwähnt wird beim Software-Taskswitchung der TSS nur benutzt, um ss und esp von Kernel und Userspace zu sichern. Alles andere interessiert den Prozessor nicht.
Titel: Re: Software Multitasking
Beitrag von: ehenkes am 12. May 2009, 22:37
Momentan bin ich für jede Klarstellung dankbar, denn vor lauter lauter baut man zum Schluss noch mehr Blödsinn ein, so dass garnichts mehr geht.

Zitat
Ich weiß nicht, wohin du deinen Kernel legst (du benutzt ja kein GRUB), aber normal würde ich das als einen kaputten Wert ansehen.
Der Kernel beginnt bei 0x8000h:
OUTPUT_FORMAT("binary")
ENTRY(RealMode)
phys = 0x00008000;
...

Zitat
...wird beim Software-Taskswitchung der TSS nur benutzt, um ss und esp von Kernel und Userspace zu sichern. Alles andere interessiert den Prozessor nicht.

Was ist mit cr3?

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

    current_task = current_task->next;
    if(!current_task) current_task = ready_queue;

    // new_task
    current_directory = current_task->page_directory;
    asm volatile("mov %0, %%cr3" : : "r" (current_directory->physicalAddr));
    tss_entry.cr3 = current_directory->physicalAddr; //notwendig?
    tss_entry.esp = current_task->esp;
    tss_entry.esp0 = (current_task->kernel_stack)+KERNEL_STACK_SIZE;

    return current_task->esp;  // return esp
}
Ist das so in Ordnung?

Titel: Re: Software Multitasking
Beitrag von: kevin am 12. May 2009, 22:43
Ah gut, dann kann das eip so natürlich stimmen. Ist nur eine ungewöhnliche Position.
Titel: Re: Software Multitasking
Beitrag von: ehenkes am 12. May 2009, 23:26
Zitat
error: action=report
info: action=report
debug: action=ignore

steht da bei mir.

Ja. ignore ist eben weniger Ausgabe als report.
Ich habe Report verwendet, ergibt mehrere 10000 Seiten Report.  :mrgreen:
Titel: Re: Software Multitasking
Beitrag von: ehenkes am 13. May 2009, 00:41
esp bei task A -> task A  :?
      0018FFC4h
    0018FFC0h
0018FFB8h
0018FFB8h
0018FFB8h
0018FFB8h
0018FFB8h
0018FFB8h
0018FFB8h
      0018FFC4h
        0018FFC8h
      0018FFC4h
      0018FFC4h
        0018FFC8h
      0018FFC4h
    0018FFC0h
0018FFB8h
0018FFB8h
    0018FFC0h
0018FFB8h
0018FFB8h
0018FFB8h
0018FFB8h
      0018FFC4h
         0018FFC8h
0018FFB8h
      0018FFC4h
        0018FFC8h
      0018FFC4h
    0018FFC0h
0018FFB8h
      0018FFC4h
    0018FFC0h
0018FFB8h
0018FFB8h
0018FFB8h
0018FFB8h
0018FFB8h
0018FFB8h
0018FFB8h
      0018FFC4h
        0018FFC8h
      0018FFC4h
      0018FFC4h
        0018FFC8h
      0018FFC4h
    0018FFC0h
0018FFB8h
0018FFB8h
0018FFB8h
0018FFB8h
0018FFB8h
0018FFB8h
0018FFB8h
      0018FFC4h
        0018FFC8h
0018FFB8h
      0018FFC4h
        0018FFC8h
      0018FFC4h
    0018FFC0h
0018FFB8h
      0018FFC4h
    0018FFC0h
0018FFB8h
0018FFB8h
0018FFB8h
0018FFB8h
      0018FFC4h
0018FFB8h
0018FFB8h
      0018FFC4h
        0018FFC8h
      0018FFC4h
    0018FFC0h
        0018FFC8h
      0018FFC4h
    0018FFC0h
0018FFB8h
0018FFB8h
0018FFB8h
0018FFB8h
0018FFB8h
0018FFB8h
0018FFB8h
      0018FFC4h
        0018FFC8h
0018FFB8h
Für mich kein Muster erkennbar.

Ich poste mal den gesamten Sourcecode:
http://www.henkessoft.de/OS_Dev/Downloads/36m.zip
Vielleicht kann mir mal jemand genau erklären, wie man mit dem Bochs Debugger heraus bekommt, wo und warum diese Ereignisse ablaufen?

Ich komme da mit der Schritt-Technik einfach nicht an die wirklich interessanten Stellen, z.B. im asm-Stub und im task_switch. Wie macht ihr das genau? Setzt man da irgendwelche Haltepunkte im Code selbst oder läuft das über Adressen? Wenn ja woher bekommt man diese?

Vielleicht liegt der Fehler auch in den 5 Gates (Code, Daten, Code, Daten, TSS): siehe gdt.c, flush.asm, descriptor_tables.c, task.c

void gdt_install()
{
    /* Setup the GDT pointer and limit */
    gdt_register.limit = (sizeof(struct gdt_entry) * NUMBER_GDT_GATES)-1;
    gdt_register.base  = (ULONG) &gdt;

    /* GDT GATES -  desriptors with pointers to the linear memory address */
    gdt_set_gate(0, 0, 0, 0, 0);                // NULL descriptor
    gdt_set_gate(1, 0, 0xFFFFFFFF, 0x9A, 0xCF); // CODE, privilege level 0 for kernel code
    gdt_set_gate(2, 0, 0xFFFFFFFF, 0x92, 0xCF); // DATA, privilege level 0 for kernel code

    //Kernel Mode:
    gdt_set_gate(3, 0, 0xFFFFFFFF, 0x9A, 0xCF); // CODE, privilege level 0 for kernel code
    gdt_set_gate(4, 0, 0xFFFFFFFF, 0x92, 0xCF); // DATA, privilege level 0 for kernel code

    //User Mode:
    //gdt_set_gate(3, 0, 0xFFFFFFFF, 0xFA, 0xCF); // CODE, privilege level 3 for user code
    //gdt_set_gate(4, 0, 0xFFFFFFFF, 0xF2, 0xCF); // DATA, privilege level 3 for user code

    write_tss   (5, 0x10, 0x0); //num, ss0, esp0

    gdt_flush((ULONG)&gdt_register); // inclusive gdt_load() in assembler code

    tss_flush(); //privilege level 3 for user mode 0x2B // in flush.asm
                 //privilege level 0 for kernel mode 0x28
}
Gate 3 und 4 werden z.Z. noch im kernel mode (privilege 0) betrieben, sollen später aber auf 3 umgesetzt werden. TSS habe ich in flush.asm auch auf Privileg 0 gesetzt?! Mit 3 geht es aber auch nicht.
Syscalls gehen auch schon, daher wurde der user mode bereits getestet, muss aber auf Privileg 0 bleiben, da ich kernel code verwende und nicht nurFunktionen über syscall aufrufe. Mit den kernel stacks und user stacks komme ich konzeptionell auch noch nicht ganz klar.
Könnte da mal jemand rein schauen? Task switch erfolgt in task_switch1 (ganz unten in task.c).
Titel: Re: Software Multitasking
Beitrag von: Jidder am 13. May 2009, 11:59
Für mich kein Muster erkennbar.
Sollte man auch nicht.

Zitat
Ich poste mal den gesamten Sourcecode:
http://www.henkessoft.de/OS_Dev/Downloads/36m.zip
Hm, da ist ja eine Menge Zeug drin. Das müsste man ausgemistet werden. Ich steig da auf jeden Fall nicht durch. Ich würde vorschlagen nicht alles aus den Tutorials/anderen Systemen in einziges Programm zu stecken.

Zitat
Vielleicht kann mir mal jemand genau erklären, wie man mit dem Bochs Debugger heraus bekommt, wo und warum diese Ereignisse ablaufen?

Ich komme da mit der Schritt-Technik einfach nicht an die wirklich interessanten Stellen, z.B. im asm-Stub und im task_switch. Wie macht ihr das genau? Setzt man da irgendwelche Haltepunkte im Code selbst oder läuft das über Adressen? Wenn ja woher bekommt man diese?

Mit einer Map-Datei. Die bekommst du, wenn du ld den Parameter -Map kernel.map übergibst. Dann stehen in der Datei kernel.map alle globalen Symbole. Da kannst du dir z.B. irq0 oder task_switch (oder isr_common_stub/irq_common_stub, wenn du diese global deklarierst) raussuchen, und die Adresse merken. Bei mir ist irq0 zum Beispiel 0x0000816a.

Wenn du in bochs im Debugger bist, kannst du da dann einen breakpoint mittels "lb 0x816a" setzen. Mit c kommst du dahin, und kannst dann mit s durchsteppen.
Titel: Re: Software Multitasking
Beitrag von: ehenkes am 13. May 2009, 18:27
Zitat
-Map kernel.map ... globale Symbole. ... irq0 zum Beispiel 0x0000816a ... bochs im Debugger bist, ... breakpoint mittels "lb 0x816a" setzen. Mit c kommst du dahin, und kannst dann mit s durchsteppen.
Super! Danke für den Tipp mit -Map. Musste ich bisher noch nicht so intensiv machen.

Zitat
Hm, da ist ja eine Menge Zeug drin.
Fast nichts. Das soll ein OS werden.  :-)

Zitat
Ich steig da auf jeden Fall nicht durch.
Schade.  :-( Bisher hast Du mir auf jeden Fall gut weiter geholfen.
Titel: Re: Software Multitasking
Beitrag von: ehenkes am 13. May 2009, 18:51
Zitat
EIP=9a07 ...

Ich weiß nicht, wohin du deinen Kernel legst (du benutzt ja kein GRUB), aber normal würde ich das als einen kaputten Wert ansehen.

PorkChicken:
Zitat
Bei mir ist irq0 zum Beispiel 0x0000816a.

 :-D :-D :-D

Es scheint noch mehr Leute zu geben, die ihren Kernel ziemlich direkt hinter dem bootloader anbringen.
Titel: Re: Software Multitasking
Beitrag von: Jidder am 13. May 2009, 19:17
Zitat
EIP=9a07 ...

Ich weiß nicht, wohin du deinen Kernel legst (du benutzt ja kein GRUB), aber normal würde ich das als einen kaputten Wert ansehen.

PorkChicken:
Zitat
Bei mir ist irq0 zum Beispiel 0x0000816a.

 :-D :-D :-D

Es scheint noch mehr Leute zu geben, die ihren Kernel ziemlich direkt hinter dem bootloader anbringen.
Ne, das ist dein OS. Nach dem Austausch deiner als Makefile getarnten Batch-Datei, war das doch recht einfach zum Laufen zu bringen ;)
Titel: Re: Software Multitasking
Beitrag von: ehenkes am 13. May 2009, 19:40
Zitat
war das doch recht einfach zum Laufen zu bringen
Na super.  :-D leider nur die Version mit task A -> task A in task_switch1
Darf ich mal fragen mit welchem Compiler Du dabei arbeitest, weil wir noch das Problem mit dem notwendigen aout-Format und dem Linker haben.

Ich habe nun das Debugging beim echten task_switch entsprechend deinem Vorschlag durchgeführt, habe _irq0 und _task_switch1 als breakpoint gesetzt, bin aber nicht an die Stelle gekommen, wo der GPF direkt auftritt. Das bringt mich momentan irgendwie nicht weiter, sitze wirklich fest.

Titel: Re: Software Multitasking
Beitrag von: Jidder am 13. May 2009, 19:45
Ich verwende einen Cross Compiler für ELF unter Windows (gcc 4.3.1), den ich selbst gebaut habe. Ich habe bei mir deswegen auch bei den Assembler-Aufruf aout durch elf ersetzt.

edit: verlesen -.-
Titel: Re: Software Multitasking
Beitrag von: ehenkes am 13. May 2009, 20:00
Zitat
Ich verwende einen Cross Compiler für ELF unter Windows (gcc 4.3.1), den ich selbst gebaut habe. Ich habe bei mir deswegen auch bei den Assembler-Aufruf aout durch elf ersetzt.
Hast Du das irgendwo beschrieben, wie man das macht? Denn das wäre wirklich interessant.
Titel: Re: Software Multitasking
Beitrag von: Jidder am 13. May 2009, 20:03
Ich hab die Anleitung (http://wiki.osdev.org/GCC_Cross-Compiler) auf osdev.org befolgt. In unserem Wiki ist aber auch eine, die genauso gut ist: http://lowlevel.brainsware.org/wiki/index.php/Cross-Compiler

Beide sollten ohne große Änderungen in einer aktuellen MinGW/MSYS Umgebung funktionieren.
Titel: Re: Software Multitasking
Beitrag von: ehenkes am 13. May 2009, 20:27
Danke für die Links!

Ich habe deinen Rat befolgt und entrümpelt (VFS, RAM disk, User Gates, Syscall alles weg): http://www.henkessoft.de/OS_Dev/Downloads/36n.zip

Die Reaktion ist noch gleich: GPF
Beim diesbezüglichen Error-Log stimmt etwas nicht mit den Daten in regs.
Nun gibt es nur noch paging, heap, tasking.
Vielleicht findest Du nun leichter durch und damit den entscheidenden Fehler.

 
Titel: Re: Software Multitasking
Beitrag von: Jidder am 13. May 2009, 21:45
Hm, mal schauen. Also ich hab natürlich noch viel mehr gefunden, was da raus kann. Ohne jetzt tief in deinem Code bohren zu wollen, hab ich einfach mal meine Entfernen-Taste etwas strapaziert und Funktion-/Block-/Zeilenweise deinen Code gelöscht. Deine main-Methode ist ein 10-Zeiler und kernel.asm ist auch nur noch ein Schatten seiner selbst.

Also ich habs soweit reduziert, dass für mich das fork() als der Übeltäter aussieht. Im Kernel forkt man nicht. Das macht auch irgendwie keinen Sinn. Ich sehe da außerdem nicht, wo du den Stack kopierst. Das kernel_directory->tables == src->tables ist auch falsch, weil die CPU in deinem aktuellen Page Directory das Dirty Flag setzen kann. (Oder wie auch immer das heißt.)

Im allgemeinen ist fork() sowieso zu kompliziert. Ein create_task tuts auch.  (Erstellt einen neuen Task, für den nur der Einsprungspunkt vorgegeben ist, nicht der ganze Prozess)

Bei mir funktionieren folgende Änderungen (an 36n.zip):

ckernel.c
#include "os.h"
#include "kheap.h"
#include "task.h"

extern page_directory_t* current_directory;
extern task_t* current_task;
extern tss_entry_t tss_entry;
extern ULONG read_eip();

void moo()
{
while(1) printformat("MOO %d", getpid()); // <-- kuh
}

void baa()
{
while(1) printformat("BAA %d", getpid()); // <- schaf
}

int main()
{
    printformat("Welcome to PrettyOS 0.07\n");

    // GDT, IDT, ISRS, IRQ, timer, keyboard, paging, enable interrupts, RAM disk, multitasking
    gdt_install();
    idt_install();
    isrs_install();
    irq_install();
    initODA();
    timer_install();
    keyboard_install();
    sti();
    paging_install();
    tasking_install();

create_task(moo);
create_task(baa);

    pODA->ts_flag = 1;

while(1) printformat("%d", getpid());
return 0;
}

task.c neue Funktion:
extern void irq_tail();
void create_task(void *entry)
{
    task_t* parent_task = (task_t*)current_task;
    page_directory_t* directory = clone_directory(current_directory);
    task_t* new_task = (task_t*)k_malloc(sizeof(task_t),0,0);

    new_task->id  = next_pid++;
    new_task->page_directory = directory;
    new_task->kernel_stack = k_malloc(KERNEL_STACK_SIZE,1,0)+KERNEL_STACK_SIZE;
    new_task->next = 0;

    task_t* tmp_task = (task_t*)ready_queue;
    while (tmp_task->next)
        tmp_task = tmp_task->next;
    tmp_task->next = new_task; // ... and extend it

unsigned int *kernel_stack = (unsigned int*)new_task->kernel_stack;

    //*(--kernel_stack) = 0x23; // ss
    //*(--kernel_stack) = USER_STACK_VIRT + USER_STACK_SIZE - 0x4;
    *(--kernel_stack) = 0x0202; // eflags = interrupts aktiviert und iopl = 0
    *(--kernel_stack) = 0x08; // cs
    *(--kernel_stack) = (unsigned int)entry; // eip

    *(--kernel_stack) = 0; // interrupt nummer
    *(--kernel_stack) = 0; // error code

    // general purpose registers
    //*(--kernel_stack) = 0; // eins von denen ist esp
    *(--kernel_stack) = 0;
    *(--kernel_stack) = 0;
    *(--kernel_stack) = 0;
    *(--kernel_stack) = 0;
    *(--kernel_stack) = 0;
    *(--kernel_stack) = 0;
    *(--kernel_stack) = 0;

    // segment registers
    *(--kernel_stack) = 0x10;
    *(--kernel_stack) = 0x10;
    *(--kernel_stack) = 0x10;
    *(--kernel_stack) = 0x10;
   
new_task->ebp = 0xd00fc0de;
new_task->esp = (unsigned int)kernel_stack;
new_task->eip = irq_tail;
}
*hust* Stackaufbau aus tyndur ^^

In isr.asm in irq_common_stub vor das mov esp, eax
global irq_tail
irq_tail:

Edit: Sorry, dass ich schon wieder eine neue Funktion/Methode vorschlage. Aber beim OS-Dev in dem Stadium ist der Weg das Ziel.
Titel: Re: Software Multitasking
Beitrag von: ehenkes am 13. May 2009, 23:54
Zuerst mal vielen Dank für Deine Mühe!  Das ist echt super. :-)

Habe alles eingebaut und folgenden Ausdruck erhalten:
Zitat
ts:
id: 1 ebp: 00000000h esp: 001FFE9Ch eip: 00000000h PD: 00000000h k_stack: 40101800h next: 40101814h

ts:
id: 2 ebp: D00FC0DEh esp: 401056E4h eip: 00008495h PD: 40102000h k_stack: 40105800h next: 40101844h

ts:
id: 3 ebp: D00FC0DEh esp: 401096BCh eip: 00008495h PD: 40106000h k_stack: 40109800h next: 00000000h


3BAA 3BAA 3BAA 3BAA 3BAA 3BAA 3BAA 3BAA 3BAA 3BAA 3BAA 3BAA 3BAA 3BAA 3BAA 3BAA 3BAA 3BAA 3BAA 3BAA 3BAA 3BAA 3BAA 3BAA 3BAA 3BAA 3BAA
:-D

Manchmal sieht es auch so aus:
Zitat
ts:
id: 3 ebp: D00FC0DEh esp: 401096E0h eip: 00008495h PD: 40106000h k_stack: 40109800h next: 00000000h

ts:
id: 1 ebp: 00000000h esp: 001FFEC0h eip: 00000000h PD: 00000000h k_stack: 40101800h next: 40101814h

ts:
id: 2 ebp: D00FC0DEh esp: 401056DCh eip: 00008495h PD: 40102000h k_stack: 40105800h next: 40101844h

ts:
id: 3 ebp: D00FC0DEh esp: 401096E0h eip: 00008495h PD: 40106000h k_stack: 40109800h next: 0000FC0DEh

ts:
id: 1 ebp: 00000000h esp: 001FFEC0h eip: 00000000h PD: 00000000h k_stack: 40101800h next: 40101814h

1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111
Ich wundere mich gerade, dass das MOO nicht auftaucht.

Wo kommt eigentlich diese Adresse her?
new_task->ebp = 0xd00fc0de;
Bin echt begeistert. Es "multitaskt"  :-)
Vielen Dank für die Starthilfe bei diesem Thema.



Titel: Re: Software Multitasking
Beitrag von: Jidder am 14. May 2009, 00:00
MOO sollte in Kombinationen mit 2en aufreten. Du könntest mal in task_switch1 das task_log auskommentieren. Dann ist diese Zeile vielleicht leichter zu finden. Wenn sie gar nicht zu finden ist, haben wir noch ein Problem. ;)

Die Variable new_task->ebp wird zur Zeit (so wie ich das sehe) nicht verwendet, deswegen hab ich sie mit irgendwas deutlich ungültigem geladen. Dann knallts wenigstens laut, wenn sie doch mal verwendet wird. Diese Adresse ist übrigens in leetspeak kodiert ;)
Titel: Re: Software Multitasking
Beitrag von: ehenkes am 14. May 2009, 00:08
Zitat
11111111111111111111111111111111111111111111111111111111111111111111111111111111
1111111111111111111111111111111111111111111111
                                               2MOO 2MOO 2MOO 2MOO 2MOO 2MOO 2MO
O 2MOO 2MOO 2MOO 2MOO 2MOO 2MOO 2MOO 2MOO 2MOO 2MOO 2MOO 2MOO 2MOO 2MOO 2MOO 2MO
 BAA 3BAA 3BAA 3BAA 3BAA 3BAA 3BAA 3BAA 3BAA 3BAA 3BAA 3BAA 3BAA 3BAA 3BAA 3BAA
3BAA 3BAA 3BAA 3BAA 3BAA 3BAA 3BAA 3BAA 3B11111111111111111111111111111111111111
11111111111111111111111111111111111111111111111111111111111111111111111111111111
11111111111111111111111111111111111111111111111111111111111111111111111111111111
O 2MOO 2MOO 2MOO 2MOO 2MOO 2MOO 2MOO 2MOO 2MOO 2MOO 2MOO 2MOO 2M1111111111111111
O 2MOO 2MOO 2MOO 2MOO 2MOO 2MOO 2MOO 2MOO 2MOO 2MOO 2MOO 2MOO 2MOO 2MOO 2MOO 2MO
O 2MOO 2MOO 2MOO 2MOO 2MOO 2MOO 2MOO 2MOO 2MOO 2MOO 2MOO 2MOO 2MOO 2MOO 2MOO 2MO
O 2MOO 2MOO 2MOO 2MOO 2MOO 2MOO 2MOO 2MOO 2MOO 2MOO 2MOO 2MOO 2MOO 2MOO 2MOO 2MO
 A 3BAA 3BAA 3BAA 3BAA 3BAA 3BAA 3BAA 3BAA 3BAA 3BAA 3BAA 3BAA 3BAA 3BAA 3BAA 3B
AA 3BAA 3BAA 3BAA 3BAA 3BAA 3BAA 3BAA 3BAA 3BAA 3BAA 3BAA 3BAA 3BAA 3BAA 3BAA 3B
AA 3BAA 3BAA 3BAA 3BAA 3BAA 3BAA 3BAA 3BAA
                                          11111111111111111111111111111111111111
11111111111111111111111111111111111111111111111111111111111111111111111111111111
111111111111111111111111111111111111111111111
                                             O 2MOO 2MOO 2MOO 2MOO 2MOO 2MOO 2MO
O 2MOO 2MOO 2MOO 2MOO 2MOO 2MOO 2MOO 2MOO 2MOO 2MOO 2MOO 2MOO 2MOO 2MOO 2MOO 2MO
O 2MOO 2MOO 2MOO 2MOO 2MOO 2MOO 2MOO 2MOO 2MOO 2MOO 2MOO 2MOO 2MOO 2MOO 2MOO 2MO
 3BAA 3BAA 3BAA 3BAA 3BAA 3BAA 3BAA 3BAA 3BAA 3BAA 3BAA 3BAA 3BAA 3BAA 3BAA 3BAA
 3BAA 3BAA 3BAA 3BAA 3BAA 3BAA 3BAA 3BAA 3BAA 3BAA 3BAA 3BAA 3BAA 3BAA 3BAA 3BAA
 3BAA 3BAA 3BAA 3BAA 3BAA 3BAA 3BAA 3BAA 311111111111111111111111111111111111111
111111111111111111111111111111
No Problem. Doch kein DOOFCODE.  :-P
Titel: Re: Software Multitasking
Beitrag von: ehenkes am 14. May 2009, 00:23
Kannst Du dazu bitte noch etwas sagen?
*(--kernel_stack) = 0x0202; // eflags = interrupts aktiviert und iopl = 0
0x0202 = 1000000010

Warum wurde Bit 1 gesetzt?

Bit 0: Carry-Flag (CF)
Bit 1: unbelegt
Bit 2: Parity-Flag (PF)
Bit 3: unbelegt
Bit 4: Auxiliary-Flag (AF)
Bit 5: unbelegt
Bit 6: Zero-Flag (ZF)
Bit 7: Sign-Flag (SF)
Bit 8: Trap-Flag (TF)
Bit 9: Interrupt-Flag (IF)
Bit 10: Direction-Flag (DF)
Bit 11: Overflow-Flag (OF)
Bit 12 und 13: I/O-Privilege Field-Flag (IOPL)
Bit 14: Nested-Task-Flag (NT)
Bit 15: unbelegt
Bit 16: Resume-Flag (RF)
Bit 17: Virtual-8086-Mode-Flag (VM)
Bit 18: Alignment Check-Flag (AC)
Bit 19: Virtual-Interrupt-Flag (VIF)
Bit 20: Virtual-Interrupt-Pending-Flag (VIP)
Bit 21: Identification-Flag (IF)
Bit 22-31: unbelegt

Hier ist auch eine interessante Seite zum Thema Multitasking:
http://www.osdever.net/tutorials/multitasking.php
Titel: Re: Software Multitasking
Beitrag von: Jidder am 14. May 2009, 00:36
Bit 1 hat zwar keine besondere Bedeutung, aber es ist immer fest auf 1 gesetzt. Ich hätte auch 0x200 nehmen können, die CPU hätte trotzdem Bit 1 gesetzt.
Titel: Re: Software Multitasking
Beitrag von: ehenkes am 14. May 2009, 00:44
Danke für die Hilfe und die Informationen.

Ich lade den funktionsfähigen Code dieser Multitasking-Versuchsversion hoch, falls andere da mal mit spielen wollen:
http://www.henkessoft.de/OS_Dev/Downloads/36p.zip

Titel: Re: Software Multitasking
Beitrag von: kevin am 14. May 2009, 18:18
Wo kommt eigentlich diese Adresse her?
new_task->ebp = 0xd00fc0de;
Ich fordere Lizenzzahlungen, das ist meine! ;)
Titel: Re: Software Multitasking
Beitrag von: ehenkes am 14. May 2009, 22:14
Zitat
Baust du das mit dem Multitasking auch ein?
Dieses Thema ist absolut faszinierend, also gehört es ins PrettyOS Tutorial. Ich möchte es aber erst verarbeiten, wenn ich die Zusammenhänge komplett verstanden habe. Der Code von James Molloy ist für eigene Versuche nicht verwertbar. Daher wurde er hier zu Recht weitgehend auf den Müll geworfen.  :-)

@PorkChicken: Du hast geschrieben, dass Du auch mein kernel.asm reduziert hast. Kannst Du das mal zeigen, oder hast Du ckernel.c gemeint?

Auf jeden Fall werde ich diesem Forum hier treu bleiben. Das Forum und die vorbildhafte Hilfestellung PorkChicken's habe ich hier gewürdigt:
http://www.henkessoft.de/OS_Dev/OS_Dev1.htm#mozTocId64850
Titel: Re: Software Multitasking
Beitrag von: Jidder am 14. May 2009, 22:37
@PorkChicken: Du hast geschrieben, dass Du auch mein kernel.asm reduziert hast. Kannst Du das mal zeigen, oder hast Du ckernel.c gemeint?
jo, das kann ich. Ich hab zur Zeit keinen Zugriff auf den PC mit dem Code, aber ich hab das mal versucht zu rekonstruieren:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; HenkesSoft 0.07 (version from Apr 18, 2009) ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;org 0x8000     ; code's start offset
[BITS 16]     ; 16 Bit Code
[global RealMode]
[extern _main] ; this is in the c file

;;;;;;;;;;;;;
; Real Mode ;
;;;;;;;;;;;;;

RealMode:
    xor ax, ax         ; set up segments
    mov es, ax
    mov ds, ax
    mov ss, ax
    mov sp, ax

    mov si, welcome
    call print_string

    cli               ; clear interrupts

    lgdt [gdtr]       ; load GDT via GDTR (defined in file "gtd.inc")

; we actually only need to do this ONCE, but for now it doesn't hurt to do this more often when
; switching between RM and PM
    in  al, 0x92      ; switch A20 gate via fast A20 port 92
    cmp al, 0xff      ; if it reads 0xFF, nothing's implemented on this port
    je .no_fast_A20
   
    or  al, 2         ; set A20_Gate_Bit (bit 1)
    and al, ~1        ; clear INIT_NOW bit (don't reset pc...)
    out 0x92, al
    jmp .A20_done
   
.no_fast_A20:         ; no fast shortcut -> use the slow kbc...
    call empty_8042 
   
    mov al, 0xD1      ; kbc command: write to output port
    out 0x64, al
    call empty_8042
   
    mov al, 0xDF      ; writing this to kbc output port enables A20
    out 0x60, al
    call empty_8042

.A20_done:
    mov eax, cr0      ; switch-over to Protected Mode
    or  eax, 1        ; set bit 0 of CR0 register
    mov cr0, eax      ;

    jmp 0x8:ProtectedMode ; http://www.nasm.us/doc/nasmdo10.html#section-10.1
 
;;;;;;;;;
; Calls ;
;;;;;;;;;

empty_8042:
    call Waitingloop
    in al, 0x64
    cmp al, 0xff      ; ... no real kbc at all?
    je .done
   
    test al, 1        ; something in input buffer?
    jz .no_output
    call Waitingloop
    in al, 0x60       ; yes: read buffer
    jmp empty_8042    ; and try again
   
.no_output:
    test al, 2        ; command buffer empty?
    jnz empty_8042    ; no: we can't send anything new till it's empty
.done:
ret

print_string:
    mov ah, 0x0E
.loop_start:
    lodsb              ; grab a byte from SI
    test al, al        ; test AL
    jz .done           ; if the result is zero, get out
    int 0x10           ; otherwise, print out the character!
    jmp .loop_start
.done:
    ret


Waitingloop:                   
    mov ebx,0x9FFFF
.loop_start:
    dec ebx     
    jnz .loop_start
    ret       
   
;;;;;;;;;;;;;;;;;;
; Protected Mode ;
;;;;;;;;;;;;;;;;;;

[Bits 32]

ProtectedMode:
    mov    ax, 0x10
    mov    ds, ax      ; data descriptor --> data, stack and extra segment
    mov    ss, ax           
    mov    es, ax
    xor    eax, eax    ; null desriptor --> FS and GS
    mov    fs, ax
    mov    gs, ax
    mov    esp, 0x200000 ; set stack below 2 MB limit

  call _main ; ->-> C-Kernel
  jmp $
   
 

;;;;;;;;;;;
; Strings ;
;;;;;;;;;;;

welcome db 'HenkesSoft 0.07 (Apr 18, 2009)', 13, 10, 0

;;;;;;;;;;;;
; Includes ;
;;;;;;;;;;;;

%include "gdt.inc"
Hab noch die welcome-Nachricht drinne gelassen, und nur das nötigste für den Wechsel in den Protected Mode. Das erleichtert das Testen, wenn man nicht immer "pm" eintippen muss, und dann warten darf, bis der Bildschirm voll mit buntem Text ist ;)

Achja, die Waitingloop hab ich aus dem [bits 32]- in den [bits 16]-Teil geschoben. Es ist ziemlich riskant 32 Bit Code aus 16 Bit Code heraus aufzurufen. In diesem Fall wird folgendes ausgeführt:
00000000  BBFFFF            mov bx,0xffff
00000003  0900              or [bx+si],ax ; <--- Das sind die Überreste vom mov ebx, 0x0009ffff
00000005  4B                dec bx
00000006  75FD              jnz 0x5
00000008  C3                ret
Es kommt auf den Kontext an, ob die zusätzliche or-Instruktion problematisch ist, oder nicht ;)

@TheThing: Ich trenn mal dein Thema ab. Der Thread ist lang genug ;)
Titel: Re: Software Multitasking
Beitrag von: ehenkes am 16. May 2009, 00:57
Ich habe das Thema Software-Multitasking hier als kleines Kapitel im Tutorial verarbeitet:
http://www.henkessoft.de/OS_Dev/OS_Dev2.htm#mozTocId114565
Hoffentlich habe ich alles richtig beschrieben.

Beim Recherchieren habe ich noch folgendes Tutorial gefunden, das relativ ähnlich arbeitet, aber schon ausgefeiltere task-Strukturen und -Funktionen hat:
http://www.osdever.net/tutorials/multitasking.php

Danke nochmals an PortChicken! Auch für die verkürzte Variante von kernel.asm. Ich schleppe da mein viertel Tutorial mit mir nach.
Titel: Re: Software Multitasking
Beitrag von: ehenkes am 18. May 2009, 21:08
Ich habe da doch noch eine Nachfrage:

Bei mir sieht das nach Vorlage tyndur so aus:
    *(--kernel_stack) = 0x0200;       // eflags = interrupts aktiviert und iopl = 0
    *(--kernel_stack) = 0x08;         // cs
    *(--kernel_stack) = (ULONG)entry; // eip
    *(--kernel_stack) = 0;            // interrupt nummer
    *(--kernel_stack) = 0;            // error code

Im Intel Manual Vol. 3, 5-18 habe ich Folgendes gefunden:
Zitat
eflags, cs, eip, error code
Die Interrupt Nr. taucht da gar nicht auf.  :?
Titel: Re: Software Multitasking
Beitrag von: Jidder am 18. May 2009, 21:34
Jo, die Reihenfolge in den Kommentaren stimmt nicht.
Titel: Re: Software Multitasking
Beitrag von: ehenkes am 18. May 2009, 21:49
Also eflags, cs, eip, error code, interrupt nr? Warum steht da nix im Intel Manual von dieser Nr.?
Titel: Re: Software Multitasking
Beitrag von: Jidder am 18. May 2009, 21:54
Weil wir die Nummer selbst auf den Stack legen. Und das wiederrum tun wir, weil wir zu faul sind, 256 vollständige Interrupthandler zu schreiben. Lieber bloß jedem Interrupt einen 2- bis 3-zeiligen Stub geben, und dann zum richtigen Handler springen.
Titel: Re: Software Multitasking
Beitrag von: ehenkes am 18. May 2009, 21:56
Ah, nun wird das klar.  :-)