Autor Thema: ISR Common Stub -> INT13  (Gelesen 10652 mal)

nnosdev

  • Beiträge: 61
    • Profil anzeigen
Gespeichert
« am: 24. January 2014, 11:42 »
Hallo Leute,

ich weiß nicht woran das hier liegt. Folgendes passiert bei mir:


%macro isr_stub 1
global isr%1
isr%1:
cli
push byte 0
push byte %1
jmp isr_common_stub
%endmacro

isr_common_stub:
; save cpu state by pushing
; main purpose registers onto the stack

pusha 

mov eax, ds
push eax

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

push esp
call Isr_Handler ; call interrupt handler
mov esp, eax

pop eax
mov ds, eax ; get old ds
        ;mov es, eax
        ;mov fs, eax
;mov gs, eax

popa

add esp, 8 ; pop error code, interrupt number
sti ; enable interrupts
iret ; interrupt return

Nachdem das Programm vom Isr_Handler zurückkehrt löst es bei der Instruktion mov ds, eax einen INT13 aus. Kommentiere ich dies Instruktion aus läuft alles soweit ohne Probleme.

Der Plan ist VOR Isr_Handler den Kernel-Mode aufzusetzen indem zuvor das verwendete DS Register gepush wird und dnach die Register entpsrechend gesetzt werden. Nach Isr_Handler will ich das alte DS wieder auslesen und in das Register schreiben.

Ich komme nicht wirklich dahinter was das Problem ist..

cpu_state* Isr_Handler(cpu_state *state) {

if (state->int_no <= 0x1F) {
dprint("[ISR] Exception captured. Kernel stopped.");

dprint("CPU State:");
dprint("     INT: %d, RING: %d, ERRCODE: 0x%X\n"
"\n"
"     EAX: 0x%X EBP: 0x%X EBX: 0x%X ECX: 0x%X\n"
"     EDI: 0x%X EDX: 0x%X EIP: 0x%X ESI: 0x%X\n"
"     ESP: 0x%X EFLAGS: 0x%X\n"
"     CS:  0x%X DS:  0x%X SS:  0x%X", state->int_no,
state->useresp, state->err_code, state->eax, state->ebp, state->ebx,
state->ecx, state->edi, state->edx, state->eip, state->esi,
state->esp, state->eflags, state->cs, state->ds, state->ss);

while (1) {
// Stop CPU
asm volatile ("cli; hlt");
}
}
// Hardware-Interrupts
if (state->int_no >= 0x20 && state->int_no <= 0x2f) {

dprint("[ISR] Hardware interrupt 0x%X received.", state->int_no);
  dprint("     INT: %d, RING: %d, ERRCODE: 0x%X\n"
          "\n"
          "     EAX: 0x%X EBP: 0x%X EBX: 0x%X ECX: 0x%X\n"
          "     EDI: 0x%X EDX: 0x%X EIP: 0x%X ESI: 0x%X\n"
          "     ESP: 0x%X EFLAGS: 0x%X\n"
          "     CS:  0x%X DS:  0x%X SS:  0x%X\n",
          state->int_no, state->useresp, state->err_code,
          state->eax,    state->ebp,     state->ebx,      state->ecx,
          state->edi,    state->edx,     state->eip,      state->esi,
          state->esp,    state->eflags,
          state->cs,     state->ds,      state->ss);

  //uint32_t tss[32] = { 0x00, 0x00, 0x10 };

// Call Scheduler
if (state->int_no == 0x20) {
state = Scheduler_Schedule(scheduler, state);
}

if (state->int_no >= 0x28) {
// TODO Keyboard driver
outb(0xA0, 0x20);
}

outb(0x20, 0x20); // End of interrupt (EOI)
return state;
}


MTGcoder

  • Beiträge: 1
    • Profil anzeigen
Gespeichert
« Antwort #1 am: 24. January 2014, 13:04 »
Poste mal einen Qemu interrupt-Log!
BENUTZTE_QEMU_BINARY -d int -kernel KERNELSoweit ich rauslesen kann scheinst du ein #gp-Fault zu haben.
bei einem struct cpu_state * kannst du nacheinander alle Register in umgekehrter reinfolge pushen:
push ebp
push edi
push esi
push edx
push ecx
push ebx
push eax
push esp
danach musst du um 4 incrementieren.
Und von eax beginnend poppen.
dann kannst du das cpu_state * durch void ersetzten.
C ändert dann automatisch das was in der Adresse drinnesteht.
mov bp:ss, 0 ;Jetzt starten wir den PC neu
jmp $ ;Falls das nicht funktionieren soll, hängen wir
Triple Fault FTW

kevin

  • Administrator
  • Beiträge: 2 767
    • Profil anzeigen
Gespeichert
« Antwort #2 am: 24. January 2014, 13:07 »
Erstmal ein allgemeiner Kommentar, der mit dem Problem nichts zu tun hat: Wenn dein Interrupthandler mit cli anfängt, machst du was falsch. Informier dich über den Unterschied zwischen Interrupt und Trap Gates und nimm das passende anstatt IF im Handler manuell zu ändern.

Das nächste ist, dass Segmentregister nur 16 Bit breit sind. Du solltest also ax statt eax benutzen. Ich bin mir nicht sicher, was dein Assembler aus mov eax, ds macht. Speziell bin ich mir unsicher, was mit den oberen 16 Bit passiert. Auch das dürfte nicht das Problem sein, weil du ja nachher beim mov ds, eax wieder eine 16-Bit-Truncation bekommst und dann egal ist, ob in den oberen 16 Bit noch Müll stand.

Ist es der erste Userspace-Entry, der schiefgeht, oder kannst du am Anfang erfolgreich switchen und der Fehler passiert später? Bei ersterem müssten wir die ursprüngliche Initialisierung des Stacks anschauen, bei letzterem den Anfang des Interrupthandlers, der ds sichert. Hilfreich wäre es auch noch, wenn du den Wert von eax beim GPF rausbekommen könntest (z.B. durch den Registerdump von qemu -d int,  oder den Errorcode).
Thou shalt not follow the NULL pointer, for chaos and madness await thee at its end.

nnosdev

  • Beiträge: 61
    • Profil anzeigen
Gespeichert
« Antwort #3 am: 26. January 2014, 20:07 »
Poste mal einen Qemu interrupt-Log!
BENUTZTE_QEMU_BINARY -d int -kernel KERNELSoweit ich rauslesen kann scheinst du ein #gp-Fault zu haben.

Das Logfile ist leer. Ich halte den Kernel an nach INT13. Hätte qemu mir was loggen sollen?

bei einem struct cpu_state * kannst du nacheinander alle Register in umgekehrter reinfolge pushen:
        push ebp
push edi
push esi
push edx
push ecx
push ebx
push eax
push esp

Das mache ich bereits. Gebe mit pusha alle register auf den stack (siehe code).




danach musst du um 4 incrementieren.

In meinem Fall um 8. Pushe die Interrupt Nummer noch zusätzlich auf den Stack..

nnosdev

  • Beiträge: 61
    • Profil anzeigen
Gespeichert
« Antwort #4 am: 26. January 2014, 20:11 »
Erstmal ein allgemeiner Kommentar, der mit dem Problem nichts zu tun hat: Wenn dein Interrupthandler mit cli anfängt, machst du was falsch. Informier dich über den Unterschied zwischen Interrupt und Trap Gates und nimm das passende anstatt IF im Handler manuell zu ändern.

Das nächste ist, dass Segmentregister nur 16 Bit breit sind. Du solltest also ax statt eax benutzen. Ich bin mir nicht sicher, was dein Assembler aus mov eax, ds macht. Speziell bin ich mir unsicher, was mit den oberen 16 Bit passiert. Auch das dürfte nicht das Problem sein, weil du ja nachher beim mov ds, eax wieder eine 16-Bit-Truncation bekommst und dann egal ist, ob in den oberen 16 Bit noch Müll stand.

Ist es der erste Userspace-Entry, der schiefgeht, oder kannst du am Anfang erfolgreich switchen und der Fehler passiert später? Bei ersterem müssten wir die ursprüngliche Initialisierung des Stacks anschauen, bei letzterem den Anfang des Interrupthandlers, der ds sichert. Hilfreich wäre es auch noch, wenn du den Wert von eax beim GPF rausbekommen könntest (z.B. durch den Registerdump von qemu -d int,  oder den Errorcode).

Ich halte mich an dieses tutorial hier:  http://www.jamesmolloy.co.uk/tutorial_html/4.-The%20GDT%20and%20IDT.html
An dieser Stelle die Interrupts abzuschalten mag vielleicht an einem Gewissen Punkt nicht empfehlenswert sein aber im Moment würde ich gern wissen warum ich einen INT13 bekomme ^^

Ich switche noch gar nicht in den Userspace. Hier will ich nur mal in den Kernel mode switchen bei einem Interrupt und den vorigen mode wieder herstellen ( nach dem interrupt ). Es passiert aber noch kein tatsächlicher switch in den user mode
« Letzte Änderung: 26. January 2014, 20:42 von nnosdev »

Svenska

  • Beiträge: 1 792
    • Profil anzeigen
Gespeichert
« Antwort #5 am: 27. January 2014, 03:52 »
Und bitte, bitte, bitte... nenne es nicht INT13. Das verwechselt sich so leicht mit dem "int 13h".
Das Gerät nennt man Exception, genauer gesagt #GP oder General Protection Fault.

nnosdev

  • Beiträge: 61
    • Profil anzeigen
Gespeichert
« Antwort #6 am: 27. January 2014, 09:34 »
:D okay dann nenne ich es ab jetzt GP. Ich verstehe aber nicht wieso ich den bekomme da ich nie vom kernel mode in einen anderen switche ..

kevin

  • Administrator
  • Beiträge: 2 767
    • Profil anzeigen
Gespeichert
« Antwort #7 am: 27. January 2014, 10:21 »
Das Logfile ist leer. Ich halte den Kernel an nach INT13. Hätte qemu mir was loggen sollen?
Falls du einen qemu benutzt, der in Wirklichkeit keiner ist, sondern ein qem-kvm (manche Distributionen haben das gemacht), dann solltest du auch noch -no-kvm übergeben, sonst wird nichts geloggt.

Ich halte mich an dieses tutorial hier:  http://www.jamesmolloy.co.uk/tutorial_html/4.-The%20GDT%20and%20IDT.html
Dann lass mich mal darauf hinweisen, dass das Tutorial von James an einigen Stellen fragwürdige Dinge macht und an anderen schlicht fehlerhaft ist. James sagt schon seit Jahren, dass er es durch etwas anständiges ersetzen will, aber er hat es bis heute noch nicht zu einer veröffentlichbaren neuen Version gebracht.

Generell empfehlen wir eher die Tutorialreihe im Lowlevel-Wiki.

Zitat
An dieser Stelle die Interrupts abzuschalten mag vielleicht an einem Gewissen Punkt nicht empfehlenswert sein aber im Moment würde ich gern wissen warum ich einen INT13 bekomme ^^
Interrupts im Kernel abschalten ist super für ein kleines Hobby-OS, das macht manches leichter. Aber Interrupt Gates machen das schon von selber und im Gegensatz zu einem cli/sti-Paar auch noch an der richtigen Stelle statt mit einem kleinen Zeitfenster im Kernelmodus, in dem Interrupts aktiviert sind.

Zitat
Ich switche noch gar nicht in den Userspace. Hier will ich nur mal in den Kernel mode switchen bei einem Interrupt und den vorigen mode wieder herstellen ( nach dem interrupt ). Es passiert aber noch kein tatsächlicher switch in den user mode
Okay. Die Frage bleibt trotzdem: Ist das der erste Switch oder passiert es erst beim zweiten Mal oder noch später? Wie sieht dein Code zum anlegen eines Tasks aus?

Und letztendlich ist die entscheidende Frage natürlich, welchen Wert eax beim #GP hat und falls er unsinnig ist, wie er dorthin kommt.
Thou shalt not follow the NULL pointer, for chaos and madness await thee at its end.

nnosdev

  • Beiträge: 61
    • Profil anzeigen
Gespeichert
« Antwort #8 am: 27. January 2014, 12:20 »
[ISR] Exception captured. Kernel stopped.
CPU State:
     INT: 13, RING: 0, ERRCODE: 0x0

     EAX: 0x1034D2 EBP: 0x1057F4 EBX: 0x0 ECX: 0x0
     EDI: 0x0 EDX: 0x0 EIP: 0x100DAC ESI: 0x0
     ESP: 0x1057B8 EFLAGS: 0x10216
     CS:  0x8 DS:  0x0 SS:  0x0

Das ist noch die letzte Meldung die ich vom Kernel bekomme bevor ich ihn anhalte.

@Tutorial: Das mag schon sein jedoch macht mir das fürs erste nichts aus. Ich will nur mal einen Kernel haben bei dem Paging funktioniert und Programme im Usermode laufen können. Ich muss noch rein wachsen in die os-entwickling ;)

@Interrupts ausschalten: Ist nur ein kleines hobby OS :P

@Switch: Ich verstehe die Frage nicht. Ich meine gar keinen switch. Der Task wird im Kernel mode gestartet und fürs erste bleibt er auch da. Im moment wird der task im kernel mode auf den Stack gepusht und sollte 1 zu 1 wiederhergestellt werden und das alles ohne jemals den kernel mode verlassen zu haben.

*
 *
 */
cpu_state* Thread_Init(Thread *self, uint32_t *entry, uint8_t *stack)
{
cpu_state new_state = {

.ds = 0,

.eax = 0,
.ebx = 0,
.ecx = 0,
.edx = 0,
.esi = 0,
.edi = 0,
.ebp = 0,
.esp = (uint32_t) stack,
.eip = (uint32_t) entry,

/* Ring-0-Segmentregister */
.cs = 0x08,
.ss = 0x20,

/* Turn on IRQ (IF = 1) */
.eflags = 0x202,

//.useresp = 0,
};

/*
* Copying the new cpu-state on the thread stack let it looks like
* the thread has been interrupted by an interrupt. That way the
* scheduler will just start scheduling this new thread.
*/
cpu_state *state = (void*) (stack + STACK_SIZE - sizeof(new_state));

*state = new_state;

return state;
}


kevin

  • Administrator
  • Beiträge: 2 767
    • Profil anzeigen
Gespeichert
« Antwort #9 am: 27. January 2014, 17:06 »
Vielleicht ist ds = 0 einfach nicht so ein geschicktes Datensegment? Mein ja nur...
Thou shalt not follow the NULL pointer, for chaos and madness await thee at its end.

nnosdev

  • Beiträge: 61
    • Profil anzeigen
Gespeichert
« Antwort #10 am: 28. January 2014, 23:38 »
Kann es sein, dass der eigenltiche Teil, nämlich das schalten in Ring 3, hier ausgelassen wurde?

Fehlt hier nicht so etwas wie das hier:

void switch_to_user_mode()
{
   // Set up a stack structure for switching to user mode.
   asm volatile("  \
     cli; \
     mov $0x23, %ax; \
     mov %ax, %ds; \
     mov %ax, %es; \
     mov %ax, %fs; \
     mov %ax, %gs; \
                   \
     mov %esp, %eax; \
     pushl $0x23; \
     pushl %eax; \
     pushf; \
     pushl $0x1B; \
     push $1f; \
     iret; \
   1: \
     ");
}

Jidder

  • Administrator
  • Beiträge: 1 625
    • Profil anzeigen
Gespeichert
« Antwort #11 am: 28. January 2014, 23:44 »
Im Abschnitt Userspace wird etwas ähnliches gemacht. Die Register CS und SS werden in der cpu_state-Struktur auf die Werte für den User Space gesetzt und die anderen Segmentregister werden im Interrupthandler geladen und der Ringwechsel passiert zusammen mit dem Taskwechsel beim IRET.

Edit: Die Tutorials solltest du übrigens nicht kombinieren, falls du das vor hast.
« Letzte Änderung: 28. January 2014, 23:49 von Jidder »
Dieser Text wird unter jedem Beitrag angezeigt.

nnosdev

  • Beiträge: 61
    • Profil anzeigen
Gespeichert
« Antwort #12 am: 29. January 2014, 11:06 »
Okay mal angenommen ich initialisiere den Task im Kernel-Mode und mache folgendes:


isr_common_stub:
; save cpu state by pushing
; main purpose registers onto the stack

pusha 

mov eax, ds
push eax

mov eax, 0x10
mov ds, eax
        mov es, eax

push esp
call Isr_Handler ; call interrupt handler
mov esp, eax

pop eax
mov ds, eax ; get old values
        mov es, eax

popa

add esp, 8 ; pop error code, interrupt number
sti ; enable interrupts
iret ; interrupt return

Also zur Info: struct cpu_state hat in den ersten 4 bytes (uint32) platz für das Register DS. Was ich hier im Grunde mache ist einen Task der im Kernelmode läuft auf den Stack pushen, DS und ES für den Kernelmode laden > Isr_Handler aufrufen > und alles wiederherstellen. Jedoch scheitert es hier bei der Instruktion nach "pop eax" bzw. das erneute Laden des Registers DS ..

Ich muss mir das dann zu hause nochmal anschauen aber ich komme nicht dahinter was hier fliegt ..


Edit: Kombinieren will ich sie nicht aber nach möglichkeit ein wenig vergleichen. Ich musste ganz schön nachdenken warum bei eurem Tutorial nach dem Interrupt Handler "mov esp, eax" steht was ja den Stack wechselt. Als ich verstanden habe WARUM das funktioniert hab ich erst verstanden wie schön das ist :D
« Letzte Änderung: 29. January 2014, 11:07 von nnosdev »

kevin

  • Administrator
  • Beiträge: 2 767
    • Profil anzeigen
Gespeichert
« Antwort #13 am: 29. January 2014, 13:04 »
Jedoch scheitert es hier bei der Instruktion nach "pop eax" bzw. das erneute Laden des Registers DS ..

Ich muss mir das dann zu hause nochmal anschauen aber ich komme nicht dahinter was hier fliegt ..
Nein, es ist nicht das erneute Laden von ds, sondern das erstmalige Laden. Ein neuer Task hat den Anfang deines IRQ-Handler-Stubs nie ausgeführt, wenn er erstellt wird. Du legst ihm seinen Stackzustand an anderer Stelle von Hand an und dann fängt er quasi in der Mitte vom IRQ-Handler an zu laufen (also nach dem call Isr_Handler; mov esp, eax eben). Und in deinem Initialisierungscode für den Stack, den du gepostet hast, setzt du ds = 0. Dass das crasht, ist kein Wunder.

Dein switch_to_user_mode() in Inline-Assembler finde ich übrigens ziemlich scary...
« Letzte Änderung: 29. January 2014, 13:24 von kevin »
Thou shalt not follow the NULL pointer, for chaos and madness await thee at its end.

nnosdev

  • Beiträge: 61
    • Profil anzeigen
Gespeichert
« Antwort #14 am: 29. January 2014, 17:21 »
Sorry falls das jetzt eine doofe Frage ist, aber wieso hört das dann nicht VOR Isr_Handler auf zu laufen? Dort schreibe ich auch den werd 0x10 in DS rein. Und meiner Meinung nach mache ich danach genau dasselbe nochmal (im Moment zumindest) oder bin ich total verpeilt hier?

kevin

  • Administrator
  • Beiträge: 2 767
    • Profil anzeigen
Gespeichert
« Antwort #15 am: 29. January 2014, 19:02 »
Ich kann deinen Gedanken im Moment nicht folgen, tut mir leid. Was soll aufhören zu laufen? Und was ist dasselbe, das du nochmal machst? 0x10 nach ds schreiben? Ich sehe das nur einmal in deinem Code. Aber wie auch immer, bevor du das erste Mal den Task laufen lassen hast und er durch einen Interrupt unterbrochen wird, läuft für diesen Task der Teil vor Isr_Handler nicht. Alles was du dort setzt, spielt also für den ersten Wechsel zu diesem Task keine Rolle.

Vielleicht muss jemand anderes übernehmen, der besser versteht, was du meinst, und der sich klarer ausdrücken kann als ich.
Thou shalt not follow the NULL pointer, for chaos and madness await thee at its end.

 

Einloggen