Autor Thema: Programm laden und ausführen  (Gelesen 26697 mal)

ehenkes

  • Gast
Gespeichert
« am: 26. April 2009, 19:38 »
Ich habe ein Task-Modul und eine RAM Disk. Wenn ich dort nun eine ausführbare Datei habe, z.B. im COM-Format o.ä., was ist dann der einfachste Weg dieses "Programm" laufen zu lassen? Gibt es dazu ein Tutorial, habe ich bisher nicht gesehen? 

kevin

  • Administrator
  • Beiträge: 2 767
    • Profil anzeigen
Gespeichert
« Antwort #1 am: 26. April 2009, 20:02 »
Ich gehe einmal davon aus, daß Multitasking schon funktioniert, d.h. die obligatorischen zwei Kerneltasks, die "a" und "b" ausgeben, laufen soweit. Wenn nein, wäre das erstmal ein erster Schritt.

Bei .com-Dateien von DOS ist der Rest einfach: Das sind flache Binaries, sobald sie im Speicher liegen, muß man also nur noch einen Task erstellen, die Segmentregister für den Task richtig setzen und eip auf das erste Byte der Datei zeigen lassen. Das eigentliche Reinspringen in den Code sollte dann der Interrupthandler übernehmen, der das auch schon bei den Kerneltasks gemacht hat.
Thou shalt not follow the NULL pointer, for chaos and madness await thee at its end.

ehenkes

  • Gast
Gespeichert
« Antwort #2 am: 26. April 2009, 20:18 »
Danke für den Hinweis. Einen laufenden Scheduler habe ich noch nicht, da gibt es noch GPF (siehe anderen Thread). Aber das Wechseln von Tasks (an der richtigen Stelle in einer while-Schleife) klappt schon gut (ge"forkte" Kernel-Tasks, da nehme ich die PID als "Farbgeber" mittels setcolor(PID,0) beim Auslesen der KeyQueue :-D )

ehenkes

  • Gast
Gespeichert
« Antwort #3 am: 16. May 2009, 01:13 »
Nachdem Multitasking nun läuft: wo bekommt man ein kleines Beispiel-Programm her? Das könnte man ja über den incbin-Trick ins Memory linken und mit memcpy an eine ausgewählte Stelle senden und dort starten.
Nachdem ich diesen Kernel-Stack-Mechanismus und das Setzen von esp und eip hoffentlich verstanden habe, wie führt tyndur ein Programm aus?
« Letzte Änderung: 16. May 2009, 09:26 von ehenkes »

bluecode

  • Beiträge: 1 391
    • Profil anzeigen
    • lightOS
Gespeichert
« Antwort #4 am: 16. May 2009, 11:24 »
Nachdem Multitasking nun läuft: wo bekommt man ein kleines Beispiel-Programm her? Das könnte man ja über den incbin-Trick ins Memory linken und mit memcpy an eine ausgewählte Stelle senden und dort starten.
Das wäre ein erster Anfang. Macht aber nach ein paar Minuten nichtmehr viel Spaß. :-D


Zitat
Nachdem ich diesen Kernel-Stack-Mechanismus und das Setzen von esp und eip hoffentlich verstanden habe, wie führt tyndur ein Programm aus?
Die ersten Programme/Treiber (tyndur & lightOS sind Mikrokernel) werden von Grub in den RAM geladen. Grub übergibt einem nach dem Laden eine Tabelle mit (unter anderem) den Startadressen, Längen und Namen der geladenen Module. Die Module sind bei beiden Kerneln ELF-Dateien. Für jedes dieser Programme wird nach dem initialisieren des Kernels ein Prozess mit einem Thread erstellt. Dann muss vielleicht noch ein bisschen ruminitialisiert werden, aber anschließend wird zum ersten Thread geswitcht und damit das ges. Multitasking angeschoben.

ELF hat seine Spezifkation zB hier gelinkt. Auch für PE/COFF gibt es Spezifikationen, nämlich hier.
Unser Wiki hat auch einen Artikel zu ELF und ein bisschen was zu PE.
« Letzte Änderung: 16. May 2009, 11:29 von bluecode »
lightOS
"Überlegen sie mal 'nen Augenblick, dann lösen sich die ganzen Widersprüche auf. Die Wut wird noch größer, aber die intellektuelle Verwirrung lässt nach.", Georg Schramm

ehenkes

  • Gast
Gespeichert
« Antwort #5 am: 16. May 2009, 12:59 »
Zitat
Das wäre ein erster Anfang.
Das würde ich gerne erstmal aufgreifen. Spaß macht zunächst das, was man plant, umsetzt und dann auch wirklich so oder ähnlich funktioniert. Daher würde ich diesen "winzigen" Schritt gehen. Ich habe mir Folgendes überlegt. In kernel.map erhalte ich ja bereits einige Einsprünge für im Kernel bereits enthaltene Funktionen. Ein einfaches Beispiel ist settextcolor(Schriftfarbe, Hintergrundfarbe), das laut kernel.map wie folgt liegt:
0x00008614                _settextcolorIch habe also mit lb 0x00008614 einen Breakpoint gesetzt und mir das in Bochs Debugger mit s 1 angeschaut:
Zitat
Next at t=145559835 (0) [0x00008614] 0008:0000000000008614 (unk. ctxt): push ebp                  ;55
Next at t=145559836 (0) [0x00008615] 0008:0000000000008615 (unk. ctxt): mov ebp, esp              ;89e5
Next at t=145559837 (0) [0x00008617] 0008:0000000000008617 (unk. ctxt): mov al, byte ptr ss:[ebp+0xc] ; 8a450c
Next at t=145559838 (0) [0x0000861a] 0008:000000000000861a (unk. ctxt): shl eax, 0x04             ;c1e004
Next at t=145559839 (0) [0x0000861d] 0008:000000000000861d (unk. ctxt): mov dl, byte ptr ss:[ebp+0x8] ; 8a5508
Next at t=145559840 (0) [0x00008620] 0008:0000000000008620 (unk. ctxt): and edx, 0x0000000f       ;83e20f
Next at t=145559841 (0) [0x00008623] 0008:0000000000008623 (unk. ctxt): or eax, edx               ;09d0
Next at t=145559842 (0) [0x00008625] 0008:0000000000008625 (unk. ctxt): mov byte ptr ds:0xcbf4, al ; a2f4cb0000
Next at t=145559843 (0) [0x0000862a] 0008:000000000000862a (unk. ctxt): pop ebp                   ;5d
Next at t=145559844 (0) [0x0000862b] 0008:000000000000862b (unk. ctxt): ret                       ;c3

mit objdump erhalte ich folgenden Code:
00000044 <_settextcolor>:
  44: 55                    push   %ebp
  45: 89 e5                mov    %esp,%ebp
  47: 8a 45 0c              mov    0xc(%ebp),%al
  4a: c1 e0 04              shl    $0x4,%eax
  4d: 8a 55 08              mov    0x8(%ebp),%dl
  50: 83 e2 0f              and    $0xf,%edx
  53: 09 d0                or     %edx,%eax
  55: a2 54 05 00 00        mov    %al,0x554
  5a: 5d                    pop    %ebp
  5b: c3                    ret   
Wäre dies ein geeignetes überschaubares "ausführbares binäres Programm", das man als weitere Tasks mit verschiedenen Farben konfigurieren und zwischen den anderen Tasks, die Texte ausgeben, ausführen könnte. Hierbei müsste man jeweils die beiden Argumente für Vordergrund- und Hintergrund-Farbe auf dem Stack anbieten.
Worauf muss man bei create_task hierbei achten? So sieht dies momentan aus:
void create_task(void* entry)
{
    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

    ULONG* kernel_stack = (ULONG*) 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) = (ULONG)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 = (ULONG)kernel_stack;
    new_task->eip = (ULONG)irq_tail;
}
Wo packt man da die beiden Argumente für die Farben hin?


bluecode

  • Beiträge: 1 391
    • Profil anzeigen
    • lightOS
Gespeichert
« Antwort #6 am: 16. May 2009, 13:04 »
Zitat
Zitat
Das wäre ein erster Anfang.
Das würde ich gerne erstmal aufgreifen. Spaß macht zunächst das, was man plant, umsetzt und dann auch wirklich so oder ähnlich funktioniert. Daher würde ich diesen "winzigen" Schritt gehen.
Jo klar, das war auch mein erster Schritt. :wink:  Ich nehme an in tyndur war das auch nicht anders.


ganz am Anfang, also vor
    *(--kernel_stack) = 0x0202; // eflags = interrupts aktiviert und iopl = 0
Bei dem ret der Funktion (settextcolor) wirst du allerdings sehr wahrscheinlich eine Exception kriegen, da die Rücksprungadresse ja nicht auf dem Stack liegt.
« Letzte Änderung: 16. May 2009, 13:07 von bluecode »
lightOS
"Überlegen sie mal 'nen Augenblick, dann lösen sich die ganzen Widersprüche auf. Die Wut wird noch größer, aber die intellektuelle Verwirrung lässt nach.", Georg Schramm

ehenkes

  • Gast
Gespeichert
« Antwort #7 am: 16. May 2009, 13:57 »
Zitat
Bei dem ret der Funktion (settextcolor) wirst du allerdings sehr wahrscheinlich eine Exception kriegen, da die Rücksprungadresse ja nicht auf dem Stack liegt.
Ich habe folgendes gemacht:
void create_task2(void* entry, ULONG arg1, ULONG arg2)
{
    //...
    *(--kernel_stack) = arg2; // arg2
    *(--kernel_stack) = arg1; // arg1

    *(--kernel_stack) = 0x0202; // eflags = interrupts aktiviert und iopl = 0
    //...
}

vor main:
void moo()
{
  while(TRUE)
  {
      settextcolor(2,0);
      printformat("MOO %d", getpid()); // <-- kuh
      settextcolor(15,0);
  }
}

void baa()
{
  while(TRUE)
  {
      settextcolor(4,0);
      printformat("BAA %d", getpid()); // <- schaf
      settextcolor(15,0);
  }
}

void color(ULONG a, ULONG b)
{
    settextcolor(a,b);
    printformat("%d", getpid()); // <- Farbe
    settextcolor(15,0);
}

in main:
create_task(moo);
create_task(baa);
create_task2(color, 5,2);

ergibt folgendes:
task baa und task moo geben aus, dann schaltet die Hintergrundfarbe auf Türkis(5) um (wahrscheinlich vorne/hinten falsch übergeben?), man sieht eine Ausgabe (PID: grüne 4 auf türkis Hintergrund), dann page fault.

Wie bekomme ich die richtige Rücksprungadresse auf den Stack? So ga z verstehe ich das aber nicht, bei baa und moo (siehe oben) klappt das doch auch.  :?
 

ehenkes

  • Gast
Gespeichert
« Antwort #8 am: 16. May 2009, 14:03 »
Sorry, hatte das while(1) vergessen, dann klappt das auch mit der Übergabe von 2 Parametern:
void color(ULONG a, ULONG b)
{
  while(TRUE)
  {
    settextcolor(a,b);
    printformat("%d", getpid()); // <- Farbe
    settextcolor(15,0);
  }
}

Zitat
OO 2MOO 2MOO 2MOO 2MOO 2MOO 2MOO 2MOO1111111111111111111111111111111111111111111
OO 2MOO 2MOO 2MOO 2MOO 2MOO 2MOO 2MOO 2MOO 2MOO 2MOO 2MOO 2MOO 2MOO 2MOO 2MOO 2M
OO 2MOO 2MOO 2MOO 2MOO 2MOO 2MOO 2MOO 2MOO 2MOO 2MOO 2MOO 2MOO 2MOO 2MOO 2MOO 2M
OO 2MOO 2MOO 2MOO 2MOO 2MOO 2MOO 2MOO 2MOO 2MOO 2M3BAA 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 444444444444444444444444444444444444444444444444444444444444444444444444444
44444444444444444444444444444444444444444444444444444444444444444444444444444444
44444444444444444444444444444444444444444444444
                                               111111111111111111111111111111111
11111111111111111111111111                     111111111111111111111111111111111
11111111111111111111111111111111111111111111111111111111111111111111111111111111
Farben vorne hinten sind ja erst mal egal.  :-D
So ist die Reihenfolge
    *(--kernel_stack) = arg1; // arg1
    *(--kernel_stack) = arg2; // arg2
richtig. (... beim Abholen will C das von hinten nach vorne)
Die Hintergrundfarbe, also Argument2 kommt allerdings nicht an, es ist immer türkis.  :? Das verstehe ich nicht.

Das war sozusagen ein erster Schritt auf das settextcolor zu. Das ist im Prinzip wie bei syscalls, wo man Makros schreibt für syscalls mit verschidener Anzahl an Argumenten.

Kann man da eine create_task schreiben für eine variable Anzahl an Argumenten hinter entry, also von 0 bis n?

James Molloy hat das in seinem Tutorial zu syscalls (chapter 10) ziemlich kompliziert gemacht:

#define DECL_SYSCALL0(fn) int syscall_##fn();
#define DECL_SYSCALL1(fn,p1) int syscall_##fn(p1);
#define DECL_SYSCALL2(fn,p1,p2) int syscall_##fn(p1,p2);
#define DECL_SYSCALL3(fn,p1,p2,p3) int syscall_##fn(p1,p2,p3);
#define DECL_SYSCALL4(fn,p1,p2,p3,p4) int syscall_##fn(p1,p2,p3,p4);
#define DECL_SYSCALL5(fn,p1,p2,p3,p4,p5) int syscall_##fn(p1,p2,p3,p4,p5);

#define DEFN_SYSCALL0(fn, num) \
int syscall_##fn() \
{ \
 int a; \
 asm volatile("int $0x80" : "=a" (a) : "0" (num)); \
 return a; \
}
etc.

« Letzte Änderung: 16. May 2009, 14:24 von ehenkes »

ehenkes

  • Gast
Gespeichert
« Antwort #9 am: 16. May 2009, 14:36 »
Obwohl ich das mit der falschen Hintergundfarbe noch nicht verstanden habe, machte ich den nächsten Schritt mit der Folge einer GPF:

kernel.map: 0x00008664  _settextcolor

    create_task(moo);
    create_task(baa);
    create_task2(0x00008664,4,2); //stimmt das so?
Jetzt dürften wir bei deiner Vorhersage sein. Wie kann ich die GPF umgehen? Denn wenn ich das so nicht schaffe, wie soll ich dann Programme ausführen?


« Letzte Änderung: 16. May 2009, 14:38 von ehenkes »

Jidder

  • Administrator
  • Beiträge: 1 625
    • Profil anzeigen
Gespeichert
« Antwort #10 am: 16. May 2009, 15:22 »
Wie kann ich die GPF umgehen? Denn wenn ich das so nicht schaffe, wie soll ich dann Programme ausführen?
Einfach nicht die Funktionen als Task aufrufen. Die Funktion weiß nichts davon, dass du die missbrauchst. Außerdem solltest du kein "ret" an dieser Stelle haben, sondern einen Systemaufruf, um den Prozess zu beenden.
« Letzte Änderung: 16. May 2009, 15:25 von PorkChicken »
Dieser Text wird unter jedem Beitrag angezeigt.

ehenkes

  • Gast
Gespeichert
« Antwort #11 am: 16. May 2009, 15:37 »
Zitat
Die Funktion weiß nichts davon, dass du die missbrauchst.
Von diesem Vorwurf distanziere ich mich ausdrücklich, denn es ist Aufgabe einer Funktion zu funktionieren.  :-D :-D :-D

Mir gefällt gerade auf, dass das vorher gehende Beispiel auch nur durch die while-Schleife rund um settextcolor(...) geklappt hat.  :roll:

Also mache ich das wie folgt:
void moo(){...}
void baa() {...}

void surprise(ULONG a, ULONG b) // die Parameter werden nicht von settextcolor verwendet??
{
  while(TRUE)  {printformat("%d", getpid()); settextcolor(15,0);}
}

int main()
{
   //...
    create_task(moo);
    create_task(baa);
    create_task2(surprise,4,2);
   //...
    while(TRUE) {settextcolor(15,0); printformat("%d", getpid());}
    return 0;
}
... und das läuft zumindest, allerdings, ohne dass die "missbrauchte" Funktion ihre Parameter verwendet. Wie schaffe ich das?  :evil:

Ach quatsch jetzt ist die Addresse ja völlig weg.  :-D

So gibt es wieder einen GPF:
void surprise(ULONG a, ULONG b)
{
  while(TRUE)
  {
      asm("jmp 0x00008664");
      printformat("%d", getpid());
      settextcolor(15,0);
  }
}
Wie kann ich settextcolor verwenden, ohne es aufzurufen?
Geht das irgendwie mit Funktions-Pointer und der Addresse (da bin ich ganz schwach)?

Wenn es mit jmp nicht geht, nimmt man eben call:
void surprise(ULONG a, ULONG b)
{
  while(TRUE)
  {
      asm("call 0x00008664");
      printformat("%d", getpid());
      settextcolor(15,0);
  }
}
Dann passiert etwas ganz Merkwürdiges: es läuft (siehe unten Ausdruck mittels copy in Bochs), aber Vorder- und Hintergrundfarbe sind beide schwarz, also beide Parameter leider noch 0. Gibt es da eine Hilfe, dass die Funktion weiß, dass sie da was abzuholen hat?  :evil:
Zitat
2MOO 2MOO 2MOO 2MOO 2MOO 2MOO 2MOO 2MOO 2MOO 2MOO 2MOO 2MOO 2MOO 2MOO 2MOO 2MOO 2MOO 2MOO 2MOO 2MOO 2MOO 2MOO 2MOO 2MOO 2MOO 2MOO 2MOO 2MOO 2MOO 2MOO 2MOO 2MOO BAA 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 444444444444444444444444444444444444444444444444444444444444444444444444444
44444444444444444444444444444444444444444444444444444444444444444444444444444444
44444444444444444444444444444444444444444444411111111111111111111111111111111111111111111111111111111111111111111111111111111
11111111111111111111111111111111111111111111111111111111111111111111111111111111
Na, da müssen wir die Parameter wohl erneut pushen?  :?
void surprise(ULONG a, ULONG b)
{
  while(TRUE)
  {
      asm volatile("push %0" : "=m" (b));
      asm volatile("push %0" : "=m" (a));
      asm volatile("call 0x00008674");
      printformat("%d", getpid());
      settextcolor(15,0);
  }
}
(volatile kann man auch weg lassen)
Das funktioniert sogar, aber nur kurz:
Zitat
2MOO 2MOO 2MOO 2MOO 2MOO 2MOO 2MOO 2MOO 2MOO 2MOO 2MOO 2MOO 2MOO 2MOO 2MOO 2MOO
 2MOO 2MOO 2MOO 2MOO 2MOO 2MOO 2MO  2MOO 2MOO 2MOO 2MOO 2MOO 2MOO 2MOO 2MOO 2MOO
 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 3BAA444444444444444444444444444444444444444444444
44444444444444444444444444444444444444444444444444444444444444444444444444444444
44444444444444444444444444444444444444444444444444444444444444444444444444444444
11111111111111111111111111111111111111111111111111111111111111111111111111111111
11111111111111111111111111111111111111111111111111111111111111111111111111111111
111111111111
             2MOO 2MOO 2MOO 2MOO 2MOO 2MOO 2MOO 2MOO 2MOO 2MOO 2MOO 2MOO 2MOO 2M
OO 2MOO 2MOO 2MOO 2MOO 2MOO 2MOO 2MOO 2MOO 2MOO 2MOO 2MOO 2MOO 2MOO 2MOO 2MOO 2M
OO 2MOO 2MOO 2MOO 2MOO 2MOO 2MOO 2MOO 2MOO 2MOO 2MOO 2MOO 2

TSS log:
esp0: 4010A000h ss0: 00000010h cr3: 00000000h eip: 00000000h eflags: 00000000h e
ax: 00000000h ecx: 00000000h edx: 00000000h ebx: 00000000h esp: 401096BCh ebp: 0
0000000h esi: 00000000h edi: 00000000h es: 00000010h cs: 00000008h ss: 00000010h
 ds: 00000010h fs: 00000010h gs: 00000010h

err_code: 00000034h address(eip): 0000849Bh
edi: 000084B0h esi: 00000005h ebp: 401056FCh eax: 401096BCh ebx: 000007BBh ecx:
000B8000h edx: 00000020h
cs: 8 ds: 16 es: 16 fs: 35031 gs 38620 ss 35031
int_no 13 eflags 00010006h useresp 00000034h

General Protection Fault >>> Exception. System Halted! <<<
:| Ist mir schon klar, dass das alles ziemlich wild ist, aber der Übergang 4->1 hat sogar geklappt. Seltsam, dass es nun ausgerechnet bei 2->3 nicht klappt. Beim ersten Durchgang geht dies problemlos, erst nach dem Einschalten dieser Überraschungsfunktion kommt der Tasswitch-Mechanismus aus dem Takt. Da staune ich jetzt wirklich. Ihr habt da sicher eine Idee.  :-)

Bei einem weiteren Versuch wieder Abbruch zwischen task 2 und 3, wie man sieht allerdings mit anderen Werten im Stack:
Zitat
2MOO 2MOO 2MOO 2MOO 2MO
TSS log:
esp0: 4010A000h ss0: 00000010h cr3: 00000000h eip: 00000000h eflags: 00000000h eax: 00000000h ecx: 00000000h edx: 00000000h ebx: 00000000h esp: 401096C0h ebp: 00000000h esi: 00000000h edi: 00000000h es: 00000010h cs: 00000008h ss: 00000010h ds: 00000010h fs: 00000010h gs: 00000010h

err_code: 0000D7F4h address(eip): 00008499h edi: 000084B0h esi: 00000220h ebp: 401056FCh eax: 401096C0h ebx: 00000AC7h ecx:
000B84D9h edx: 00000020h cs: 8 ds: 16 es: 16 fs: 16 gs 0 ss 34135
int_no 13 eflags 00010016h useresp 4010D7F4h

General Protection Fault >>> Exception. System Halted! <<<
« Letzte Änderung: 16. May 2009, 16:37 von ehenkes »

Jidder

  • Administrator
  • Beiträge: 1 625
    • Profil anzeigen
Gespeichert
« Antwort #12 am: 16. May 2009, 17:27 »
Zitat
Bei dem ret der Funktion (settextcolor) wirst du allerdings sehr wahrscheinlich eine Exception kriegen, da die Rücksprungadresse ja nicht auf dem Stack liegt.
Ich habe folgendes gemacht:
void create_task2(void* entry, ULONG arg1, ULONG arg2)
{
    //...
    *(--kernel_stack) = arg2; // arg2
    *(--kernel_stack) = arg1; // arg1

    *(--kernel_stack) = 0x0202; // eflags = interrupts aktiviert und iopl = 0
    //...
}
Eine C-Funktion erwartet vor den Argumenten noch die Rücksprungadresse auf dem Stack. Wenn die Funktion nicht zurückkehrt, kann das natürlich ein beliebiger Wert sein.
Dieser Text wird unter jedem Beitrag angezeigt.

ehenkes

  • Gast
Gespeichert
« Antwort #13 am: 16. May 2009, 18:49 »
Bei call wird die Rücksprungadresse doch automatisch auf dem Stack abgelegt?

kernel.map:
0x00008330   _surprise
0x00008484   _settextcolor

void surprise(ULONG a, ULONG b)
{
  while(TRUE)
  {
      asm ("push %0" : "=m" (b));
      asm ("push %0" : "=m" (a));
      asm ("call 0x00008484");
      printformat("%d", getpid());
      settextcolor(15,0);
  }
}

Normaler Aufruf der Funktion settextcolor(15,0) in task1:
Zitat
| STACK 0x001fffdc [0x00008423]
 | STACK 0x001fffe0 [0x0000000f]
 | STACK 0x001fffe4 [0x00000000]

Normaler Aufruf der Funktion settextcolor(15,0) in task2:
Zitat
| STACK 0x401057e0 [0x000082e9]
 | STACK 0x401057e4 [0x0000000f]
 | STACK 0x401057e8 [0x00000000]

Normaler Aufruf der Funktion settextcolor(4,0) in task3:
Zitat
| STACK 0x401097e0 [0x00008308]
 | STACK 0x401097e4 [0x00000004]
 | STACK 0x401097e8 [0x00000000]

Aufruf der Funktion settextcolor(...) in surprise über asm ("call 0x00008484"); in task 4:
Zitat
| STACK 0x4010d7b0 [0x00008341]
 | STACK 0x4010d7b4 [0x00000004]
 | STACK 0x4010d7b8 [0x123890ab]
 | STACK 0x4010d7bc [0x00000004]
 ... immer abwechselnd so weiter ...
 | STACK 0x4010d7ec [0x00008330]
Hier sieht man nur türkis, die Zahl 4 sieht man nicht.

Zitat
| STACK 0x4010d670 [0x00008363]
 | STACK 0x4010d674 [0x0000000f]
 | STACK 0x4010d678 [0x00000000]
Hier sieht man türkis Hintergrund und in rot die Zahl 4.
Das ist die Umschaltung auf  weiß/schwarz nach Ausgabe der Zahl:
printformat("%d", getpid()); settextcolor(15,0); innerhalb surpirse(...)

Hier gibt es also zwei Zustände. Der hintere ist o.k., der erste nicht. 
Da stört dieses 0x123890ab an der Stelle der Hintergrundfarbe.
Nun verstehe ich auch den Blink-Vorgang der Zahl 4.  :-D

Beim zweiten Durchgang durch Task 2 ist alles o.k.:
Umschalten auf Grün:
Zitat
| STACK 0x401057e0 [0x000082ca]
 | STACK 0x401057e4 [0x00000002]
 | STACK 0x401057e8 [0x00000000]
Umschalten auf weiß:
Zitat
| STACK 0x401057e0 [0x000082e9]
 | STACK 0x401057e4 [0x0000000f]
 | STACK 0x401057e8 [0x00000000]

Plötzlich kommt zwischen 2 und 3 etwas task 4 dazwischen!  :? Chaos :?
Task 3 läuft dann aber weiter und schaltet dann auch wieder auf 4 mit den zwei Zuständen:
falsch:
Zitat
| STACK 0x4010ca08 [0x00008341]
 | STACK 0x4010ca0c [0x00000004]
 | STACK 0x4010ca10 [0x123890ab]
richtig:
Zitat
| STACK 0x4010c9f8 [0x00008363]
 | STACK 0x4010c9fc [0x0000000f]
 | STACK 0x4010ca00 [0x00000000]
Na immerhin liegt ein while(1) zwischen den Parametern und meinem Push-Vorgang:

void surprise(ULONG a, ULONG b)
{
  while(TRUE)
  {

      asm ("push %0" : "=m" (b));
      asm ("push %0" : "=m" (a));

So geht das mehrere Durchläufe weiter. Es erfolgt kein Absturz.
Ich breche den Debug-Vorgang ab.

Lasse ich es normal laufen, kommt es genau bis 1-2-3-4-1-2-GPF.
Hat also auch etwas mit Geschwindigkeit zu tun, also dynamische Prozesse.

Kannst Du mir einen Tipp geben, wie die beiden Parameter sicher die while-Schleife überleben?
Was ist dieses 0x123890ab? Sieht "magic" aus. Ich habe gesucht:
#define HEAP_MAGIC  0x123890ABDie Kernel-Stacks befinden sich ja auf dem Heap. Liegt daran, dass k_malloc Platz auf dem Heap sucht, sobald einer da ist (Erbe von James Molloy's Code). Ich habe nach diesem Wert in kheap.h/kheap.c gesucht:
Die Zahl wird dort als magische Zahl in Header/Footer von Holes verwendet.
Es wird im Heap-Code als Sanity-Check verwendet:
    // Sanity checks.
    ASSERT(header->magic == HEAP_MAGIC);
    ASSERT(footer->magic == HEAP_MAGIC);
Da kommt also in task 4 etwas quer gehuscht.  :-o
« Letzte Änderung: 16. May 2009, 18:54 von ehenkes »

ehenkes

  • Gast
Gespeichert
« Antwort #14 am: 16. May 2009, 19:18 »
Es lag natürlich mal wieder an meinen gigantischen Fähigkeiten bezüglich AT&T inline Assembler Syntax. Wer hier auch Probleme hat, dies ist die richtige Stelle zum Lesen: http://www.ibiblio.org/gferg/ldp/GCC-Inline-Assembly-HOWTO.html#s3

Die Grenze zur while-Schleife hatte ich auch noch falsch gezogen. So läuft es erstmal durch wie gewollt, also ohne GPF in der zweiten Runde.
Aber unten das Debugging zeigt, dass da noch dieses üble Grundproblem besteht:

void surprise(ULONG a, ULONG b)
{
  asm volatile ("push %0" :/* no output registers */: "r" (b));
  asm volatile ("push %0" :/* no output registers */: "r" (a));
  while(TRUE)
  {
      asm volatile ("call 0x00008484");
      printformat("%d", getpid());
      settextcolor(15,0);
  }
}
Aber alles hat sein Gutes. Der Debug-Prozess oben hat mir mehr Einsichten in die Abläufe auf dem Stack rund um C-Funktionen und Parameter gegeben.

Nimmt man besser "r" oder "m"? Es geht beides.

0x00008484  _settextcolor
Debugger in task 4:

schlechter Zustand: magic heap number (k.A. wo die da herkommt) geistert in Stack herum und verdrängt die Hintergrundfarbe. Rücksprungadresse und Vordergrundfarbe (rot=4) sind vorhanden.
| STACK 0x4010d7e0 [0x00008343]
 | STACK 0x4010d7e4 [0x00000004]
 | STACK 0x4010d7e8 [0x123890ab]
 | STACK 0x4010d7ec [0x00008330]
 | STACK 0x4010d7f0 [0x00000008]
 | STACK 0x4010d7f4 [0x00000000]
 | STACK 0x4010d7f8 [0x00000002]
 | STACK 0x4010d7fc [0x00000004]
 | STACK 0x4010d800 [0x123890ab]
 | STACK 0x4010d804 [0x4010cff4]
 | STACK 0x4010d808 [0x123890ab]
 | STACK 0x4010d80c [0x00000001]
 | STACK 0x4010d810 [0x000007f8]
 | STACK 0x4010d814 [0x00000000]
 | STACK 0x4010d818 [0x00000000]
 | STACK 0x4010d81c [0x00000000]


Hier ist o.k.: Rücksprungadresse, Vordergundfarbe, Hintergrundfarbe
| STACK 0x4010d7d0 [0x00008365]
 | STACK 0x4010d7d4 [0x0000000f]
 | STACK 0x4010d7d8 [0x00000000]
 | STACK 0x4010d7dc [0x4010d7f4]
 | STACK 0x4010d7e0 [0x00008343]
 | STACK 0x4010d7e4 [0x00000004]
 | STACK 0x4010d7e8 [0x123890ab]
 | STACK 0x4010d7ec [0x00008330]
 | STACK 0x4010d7f0 [0x00000008]
 | STACK 0x4010d7f4 [0x00000000]
 | STACK 0x4010d7f8 [0x00000002]
 | STACK 0x4010d7fc [0x00000004]
 | STACK 0x4010d800 [0x123890ab]
 | STACK 0x4010d804 [0x4010cff4]
 | STACK 0x4010d808 [0x123890ab]
 | STACK 0x4010d80c [0x00000001]
Aber schon mal ein netter Anfang.

So sieht mein Log der Task-List aus:
id: 1 ebp: 00000000h esp: 00000000h eip: 00000000h PD: 00000000h k_stack: 40101800h next: 40101814h
id: 2 ebp: D00FC0DEh esp: 401057C0h eip: 00008299h PD: 40102000h k_stack: 40105800h next: 40101844h
id: 3 ebp: D00FC0DEh esp: 401097C0h eip: 00008299h PD: 40106000h k_stack: 40109800h next: 40101874h
id: 4 ebp: D00FC0DEh esp: 4010D7B8h eip: 00008299h PD: 4010A000h k_stack: 4010D800h next: 00000000h
Jede Task hat einen Kernel Stack und eine eigene Page Directory.

Den eip 8299h verstehe ich nicht.

                0x000082f6                _baa
                0x00008330                _surprise
                0x000082b8                _moo

Der "D00FC0DE" hat sich also auch irgendwo manifestiert.  :-D
« Letzte Änderung: 16. May 2009, 19:53 von ehenkes »

Jidder

  • Administrator
  • Beiträge: 1 625
    • Profil anzeigen
Gespeichert
« Antwort #15 am: 16. May 2009, 20:03 »
Bei call wird die Rücksprungadresse doch automatisch auf dem Stack abgelegt?
Ja, aber task_create macht keinen call. Wenn der Task das erste Mal aktiv wird, dann wird der in task_create erstellte Stack abgebaut, und im Laufe dessen landet der Kontrollfluss am Anfang der Funktion surprise.

Zu diesem Zeitpunkt (so die C-Aufrufkonvention) muss oben auf dem Stack die Rücksprungadresse liegen und darunter die Parameter.

In dem jetzigen Zustand wird allerdings arg1 von der Funktion für die Rücksprungadresse gehalten, a ist arg2, und b liegt in einem nicht definitierten Stackbereich (bzw. wäre "arg3"). Wenn du task_create2(surprise, Vordergrundfarbe, Hintergrundfarbe) aufrufst, dann landet in a also übergebene Hintergrund-Farbe.

Wo kommt jetzt also die 0x123890ab her: Als du mit malloc() den Stack angelegt hast, hat diese Funktion den Wert 0x123890ab an die Stelle geschrieben, wo deine surprise-Funktion den Parameter b erwartet hat.

Das hast du bereits früh gemerkt, aber nicht den korrekten Schluss gezogen:
dann schaltet die Hintergrundfarbe auf Türkis(5) um (wahrscheinlich vorne/hinten falsch übergeben?)
Türkis ist nicht 5 sondern 0x0b, und zwar genau das 0xb von 0x123890ab.

Direkt prüfen kannst du das, indem du einfach mal a und b ausgibst.

Deine beiden Stacks aus dem letzten Post sind übrigens derselbe zu unterschiedlichen Zeitpunkten.

Warum rufst du eigentlich nicht einfach settextcolor(a, b) auf, sondern bastelst am Stack rum und jonglierst mit Konstanten?
« Letzte Änderung: 16. May 2009, 20:09 von PorkChicken »
Dieser Text wird unter jedem Beitrag angezeigt.

ehenkes

  • Gast
Gespeichert
« Antwort #16 am: 17. May 2009, 01:01 »
Zitat
Zu diesem Zeitpunkt (so die C-Aufrufkonvention) muss oben auf dem Stack die Rücksprungadresse liegen und darunter die Parameter.
"Oben und unten" habe ich verdreht. Dieses Bild muss man sich bezüglich des Codes in create_task auf dem Kopf vorstellen:
http://www.a-m-i.de/tips/stack/stackframe.jpg

Also es funktioniert jetzt so:
    *(--kernel_stack) = arg2; // arg2
    *(--kernel_stack) = arg1; // arg1
    *(--kernel_stack) = 0x0; // return address dummy

    *(--kernel_stack) = 0x0202; // eflags = interrupts aktiviert und iopl = 0
void surprise(ULONG a, ULONG b)
{
  asm volatile ("push %0" :/* no output registers */: "r" (b));
  asm volatile ("push %0" :/* no output registers */: "r" (a));
  while(TRUE)
  {
      asm volatile ("call 0x00008484");
      printformat("%d", getpid());
      settextcolor(15,0);
  }
}
Wenn ich es richtig verstanden habe, kommt "von oben gesehen" zuerst die Rücksprungadresse, dann die Argumente 1 ... n, richtig?

Warum muss ich diese dummy return address da in create_task2 ablegen, wogegen ich dies in create_task nicht machen muss? Diese Rücksprungadresse, die ich auf 0x0 gesetzt habe, wird ja auch nicht verwendet. So ganz habe ich das noch nicht verstanden. Liegt das daran, dass ich "call 0x00008484" anstelle settextcolor(...) verwende, so dass ich sowohl die Parameter als auch die Rücksprungadresse manuell anliefern muss? Ich hatte ja nur die Parameter geliefert.

Das kann man ja schnell ausprobieren:

Der einfache Fall (ohne selbst gepushte Rücksprungadresse und Argumente, das erledigt alles der Aufruf settextcolor):
    //TEST
    //*(--kernel_stack) = arg2; // arg2
    //*(--kernel_stack) = arg1; // arg1
    //*(--kernel_stack) = 0x0; // return address dummy

    *(--kernel_stack) = 0x0202; // eflags = interrupts aktiviert und iopl = 0
void surprise(ULONG a, ULONG b)
{
  //asm volatile ("push %0" :/* no output registers */: "r" (b));
  //asm volatile ("push %0" :/* no output registers */: "r" (a));
  while(TRUE)
  {
      //asm volatile ("call 0x00008484");
      settextcolor(4,2);//TEST
      printformat("%d", getpid());
      settextcolor(15,0);
  }
}
Der komplizierte Fall:
    *(--kernel_stack) = arg2; // arg2
    *(--kernel_stack) = arg1; // arg1
    *(--kernel_stack) = 0x0; // return address dummy

    *(--kernel_stack) = 0x0202; // eflags = interrupts aktiviert und iopl = 0
void surprise(ULONG a, ULONG b)
{
  asm volatile ("push %0" :/* no output registers */: "r" (b));
  asm volatile ("push %0" :/* no output registers */: "r" (a));
  while(TRUE)
  {
      asm volatile ("call 0x00008484");
      printformat("%d", getpid());
      settextcolor(15,0);
  }
}
Beide ergeben den gleichen Ablauf. Nun habe ich endlich ganz klar verstanden, was der Aufruf einer C-Funktion auf dem Stack bewirkt.
Bezüglich des Ablaufs von Programmen - aber auch Funktionen - frage ich mich gerade, wo genau die lokalen Variablen auf dem Stackframe hin gehören.

Zitat
Warum rufst du eigentlich nicht einfach settextcolor(a, b) auf, sondern bastelst am Stack rum und jonglierst mit Konstanten?
Gute Frage. Ich wollte den Mechanismus für das Ausführen von Code - sprich von Programmen - auf PrettyOS antesten. Dafür hatte ich mir settextcolor(...) ausgesucht, weil diese Funktion eine leicht sichtbare Funktion hat und darüber hinaus auch keine weiteren Funktionen in ihrem Inneren aufruft, so dass zwischen entry und ret nur wenige Schritte liegen, die man leicht per Debugger kontrollieren kann. Vielleicht bin ich da auf dem Holzweg.

Die nachstehende Aussage hat mich nach dem Erfolg mit "moo" und "baa" beim task switch dazu bewogen, auch das Ausführen von Code an allen möglichen Stellen zu testen:
Zitat
Bei .com-Dateien von DOS ist der Rest einfach: Das sind flache Binaries, sobald sie im Speicher liegen, muß man also nur noch einen Task erstellen, die Segmentregister für den Task richtig setzen und eip auf das erste Byte der Datei zeigen lassen. Das eigentliche Reinspringen in den Code sollte dann der Interrupthandler übernehmen, der das auch schon bei den Kerneltasks gemacht hat.
Wie auch immer, ich lerne momentan eine Menge über C-Funktionen. So "low level" schaue ich mir diese selten an.

Noch mal die Kernfrage: wie kann ich aus dem vorliegenden einfachen Modell möglichst einfach das Ausführen von Code - durch incbin auf eine RAM disk oder sonst etwas eingeschleust, noch kann ich nichts von Platte laden, oder bereits vorhanden - bewerkstelligen?  :?

« Letzte Änderung: 17. May 2009, 11:53 von ehenkes »

ehenkes

  • Gast
Gespeichert
« Antwort #17 am: 17. May 2009, 15:23 »
Kleine Ergänzung:
Anstelle der Konstanten kann man einfacher das Label verwenden, da sich die dahinter liegende Adresse bei Veränderungen im Code verschieben kann:
asm volatile ("call _settextcolor");

ehenkes

  • Gast
Gespeichert
« Antwort #18 am: 17. May 2009, 16:54 »
Ich teste gerade folgenden Ansatz:

Ein ASM-File wird assembliert:
nasmw -O32 -f bin file_data.asm -o file_data.datund mittels incbin in den kernel eingeschleust:
global _file_data_start
global _file_data_end
_file_data_start:
incbin "file_data.dat"
_file_data_end:
Dann wird es als Task 5 aufgerufen:
void test()
{
  while(TRUE)
  {
      asm volatile ("call _file_data_start");
      printformat("%d", getpid());
      settextcolor(15,0);
  }
}

task_t* task5 = create_task0(test);

Ich fahre hierbei zwei Varianten:
a) mit dummy return address auf stack (also wie bei task4)
b) ohne dummy return address auf stack (also wie bei task2 "baa" und task3 "moo")

Mit asm-File:
[BITS 32]
start:
   Mov [0x000b8000], byte 'T'
   Mov [0x000b8002], byte 'e'
   Mov [0x000b8004], byte 's'
   Mov [0x000b8006], byte 't'
   Ret
klappt es, allerdings bisher nur im Debugger sichtbar, weil ansonsten zu schnell. Dennoch super.  :-)

Da wollte ich genau hin! Nun kann ich von außen mit NASM erstellte Programme in einer Task ausführen. Ich habe das mal hier beschrieben:
http://www.henkessoft.de/OS_Dev/OS_Dev2.htm#mozTocId188270

« Letzte Änderung: 17. May 2009, 22:55 von ehenkes »

ehenkes

  • Gast
Gespeichert
« Antwort #19 am: 18. May 2009, 21:54 »
Frage: Wie führt man programme richtig aus? Die haben ja auch noch Datenbereiche, Ein- und Ausgaben, ... . Ich habe diesbezüglich noch kein Tutorial oder Wiki gefunden, eigentlich verwunderlich, wo das Ausführen von Programmen die Hauptaufgabe von OS ist.  :-D

 

Einloggen