Autor Thema: interrupthandler - iret: return CS selector null  (Gelesen 3881 mal)

Gotbread

  • Beiträge: 5
    • Profil anzeigen
Gespeichert
« am: 07. April 2010, 12:57 »
Hallo :)

ich habe ein problem mit meinem interupthandler. die
IDT ist richtig aufgesetzt, aber wenn ich die interrupts aktiviere,
bekomme ich diese meldung, und das OS hängt sich auf.

interrupt_stub für den IRQ0
cli
push 0
push 32
jmp irq_common_stub
// ....
pusha
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
add esp, 8
iret

der dazugehörige (minimale) C-handler

unsigned irq_handler(regs* r)
{

    if (r->int_no >= 40)
        outportb(0xA0, 0x20);

    outportb(0x20, 0x20);

    return (unsigned)r;
}

komischerweise ist "r->int_no" laut bochs 0x212.
wahrscheinlich ist die regs struktur beschädigt.

mein verdacht ist, dass am anfang:
cli
push 0
push 32
jmp irq_common_stub

nur 2 Bytes statt 2 DWords gepusht werden. der disassembler sagt:

cli                     FA
push 0             6A 00 ??
push 32           6A 29 ??
mp irq_common_stub

ich nutze den msvc-inline-assembler. wie kann ich den
dazu bringen, DWORDs zu pushen? "oush dword 0" geht nicht :(

danke im voraus

erik.vikinger

  • Beiträge: 1 277
    • Profil anzeigen
Gespeichert
« Antwort #1 am: 07. April 2010, 14:30 »
Hallo,


ich habe ein problem mit meinem interupthandler.
Das ist eine wirklich sehr konkrete Problembeschreibung.
Da muss ich gleich mal schauen ob meine Kristallkugel heute noch ein bisschen Lust hat.

die IDT ist richtig aufgesetzt
Sicher? Definiere Bitte "richtig".

aber wenn ich die interrupts aktiviere, bekomme ich diese meldung
Welche Meldung?

und das OS hängt sich auf.
Das tut jedes OS von Zeit zu Zeit, ist also völlig normal und nicht sonderlich besorgniserregend. Erst das Überschreiten einer bestimmten, OS-Abhängigen (dieses Kriterium wird auch gerne zur Unterscheidung der vielen OSe untereinander benutzt), Häufigkeit ist eventuell ein potentielles Warnzeichen.

wie kann ich den dazu bringen, DWORDs zu pushen? "oush dword 0" geht nicht :(
Was besseres fällt mir auf Anhieb auch nicht ein (kenne mich mit den MS-Produkten aber auch nicht sonderlich gut aus). Vielleicht geht auch die MASM-Syntax. Die Hilfe-Funktion sollte da bestimmt weiterhelfen können.


Grüße
Erik
Reality is that which, when you stop believing in it, doesn't go away.

Gotbread

  • Beiträge: 5
    • Profil anzeigen
Gespeichert
« Antwort #2 am: 07. April 2010, 17:12 »
hm zu wenig infos. sorry :(

das die IDT stimmt, sehe ich durch den bochs-debugger.
ich kann einen haltepunkt an der stelle des "cli" setzen,
(am anfang des interrupt-stubs)
der dann auch angesprungen wird, und zwar der IRQ0,
also der timer.

danach kann ich schritt für schritt durch den handler laufen,
bis zum iret. nach dem iret kommt sofort wieder eine exception,
nämlich eine generell protecton fault (0x0D).

auch hier kann ich wieder in den handler reinspringen. sobald
er jedoch beim iret angelangt ist, fliegt die nächste GPF.

in dieser endlos-schleife bleibt er dann, und das ausgabelog
meldet mir jedesmal "iret: return CS selector null"

da der fehler auch mit einem minimalen handler auftritt, muss
der fehler im interrupt-stub liegen.

der exceptionhandler sieht wie folgt aus: (gekürzt)
void fault_handler(regs* r)
{
    if (r->int_no < 32)
    {
        // fehler anzeigen und anhalten.
    }
}

diese bedingung < 32 tritt nie auf, int_no ist 0x10002
(obwohl der fehlercode eigentlich 0xD sein sollte).

beim anfang des interrupt-stubs, ist der stackpointer bei
0x1ffe8c, im kernel-stack.
nach den beiden push's zeigt esp auf 0x1ffe84, es wurden
wirklich 2 DWords gepusht.

danach verfolge ich das ganze bis vor den aufruf des C-handlers.
esp hat hier den wert 0x1ffe64, und wird als parameter
übergeben (mov eax, esp, push eax)

diesen wert kann ich mir auch ausgeben lassen, allerdings
enthällt CS hier komischerweise den wert 0x8A32.

ein dump der stelle 0x1ffe64 ergibt:

00000000 00000000 00000010 00000010
C8008200 FE84FE9C 00130000 0001C40B
00000020 00000000 00008BEF 00000008
00000212 000016A7 001FFED4 00008A32
0000BA84 00000000 00000000 0000CDBC

dies passt jedoch nicht in die struktur regs,
hier scheinen werte verrutscht zu sein.

struct regs
{
unsigned int gs, fs, es, ds;
unsigned int edi, esi, ebp, esp, ebx, edx, ecx, eax;
unsigned int int_no, err_code;
unsigned int eip, cs, eflags, useresp, ss;
};

padding ist natürlich ausgestellt.

erik.vikinger

  • Beiträge: 1 277
    • Profil anzeigen
Gespeichert
« Antwort #3 am: 07. April 2010, 21:02 »
Hallo,


in dieser endlos-schleife bleibt er dann, und das ausgabelog meldet mir jedesmal "iret: return CS selector null"
Welchen Wert hat CS vor dem ersten Interrupt? Wie kommst Du in den PM? Könnte ja sein das da wirklich ein Problem ist.

da der fehler auch mit einem minimalen handler auftritt, muss der fehler im interrupt-stub liegen.
Legst Du für die Nicht-Exception-Interrupts einen Pseudo-Error-Code auf den Stack? Viele machen das um im generischen Interrupthandler immer einen identischen Stack-Aufbau zu haben.
Räumst Du den Stack auch wieder richtig ab (vor dem iret)? Also auch die Interrupt-Nummer und den Fehlercode. Wimre sollte unmittelbar vor dem iret der aktuelle esp auf den user-eip zeigen.

allerdings enthällt CS hier komischerweise den wert 0x8A32.
Du meinst in der Struktur? Könnte es sein das die doch irgendwie falsch befüllt wird.

dies passt jedoch nicht in die struktur regs, hier scheinen werte verrutscht zu sein.

struct regs
{
unsigned int gs, fs, es, ds;
unsigned int edi, esi, ebp, esp, ebx, edx, ecx, eax;
unsigned int int_no, err_code;
unsigned int eip, cs, eflags, useresp, ss;
};

padding ist natürlich ausgestellt.
Ich würde das padding an lassen und alles als 32Bit-Wert betrachten. CS und SS werden von der CPU auch als 32Bit-Wert auf den Stack gelegt.


Ich bin mir nicht sicher aber ich vermute Du übersiehst beim Einzelschritt-Debuggen irgend eine Kleinigkeit. Versuche es noch mal bis Du das Problem siehst. Bewaffne Dich mit Papier und Bleistift und lass Deine Brain-CPU mitlaufen. Sorry, aber einen besseren Tipp kann ich Dir momentan echt nicht geben.


Grüße
Erik
Reality is that which, when you stop believing in it, doesn't go away.

Gotbread

  • Beiträge: 5
    • Profil anzeigen
Gespeichert
« Antwort #4 am: 07. April 2010, 23:29 »
nach langwiriges debuggen habe ich einen fehler gefunden.
pusha hat nur 16bit-register gepusht, richtig wäre pushad gewesen.
einige assembler ignorieren das, einige anscheinend nicht.

nun stimmt auch die "regs" struktur wieder :)

in den protected mode komme ich via

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

jmp 0x8:ProtectedMode

[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, 0x1FFF00 ; set stack below 2 MB limit

da bochs seine address-anzeige auf 32 Bit erweitert, sieht man dass
er sich im protected-mode befindet. außerdem ist dieser teil
des codes nicht von dem compilerwechsel betroffen
(vor dem wechsel funktionierte das ganze komischerweise).

da sollte der fehler also nicht liegen.

vor dem cli (am anfang vom IRQ0 handler)

hat CS den wert 0x08 (DS, SS und ES sind 0x10). diese werte
stimmen mit der GDT überein.

Zitat von: erik.vikinger
Legst Du für die Nicht-Exception-Interrupts einen Pseudo-Error-Code auf den Stack? Viele machen das um im generischen Interrupthandler immer einen identischen Stack-Aufbau zu haben.

ja, im falle des IRQ0 lege ich zuerst 0 (fehlercode, DWORD), und dann
0x20 (nummer des interrupt, DWORD) auf den stack.

vor dem ersten push am anfang des stubs, also direkt am anfang des
interrupts zeigt esp auf 0x1ffe70. nachdem die struktur vollständig
auf dem stack liegt, und bevor sie an den C-handler übergeben wird,
enhält sie folgende werte:

gs : 00000000
fs : 00000000
es : 00000010
ds : 00000010
edi: 00008200 // basisaddresse des C-kernels, vllt hat edi solange überlebt
esi: 0000c800
ebp: 001ffe84
esp: 001ffe68
ebx: 00000000
edx: 00000013
ecx: 0000c40b
eax: 00000be4
int_nummer: 00000020 // stimmt
err_code: 00000000 // stimmt auch
eip: 0000b7af
cs : 00000008 // stimmt
flags: 00000206
useresp: 000b8000
ss : 00000be4

dieses gebilde liegt bei 0x1ffe38, d.h. der esp ist seit begin des interrupts
14 DWords gewandert. diese zahl entspricht der register (struct regs),
die manuell auf den stack gelegt werden.

bevor "IRET" ausgeführt wird, zeigt esp wieder auf 0x1ffe70.

danach fliegt sofort die exception, d.h. ich lande mit dem debugger wieder
beim "CLI" eines interrupt-stubs, (0xD -> GPF)

nach dem IRET und vor dem CLI haben die segmentregister jedoch
korrekte werte (CS = 0x8, DS, SS, ES = 0x10) ??

wenn ich im C-handler die regs-struktur dumpe, erhalte ich auch die
selben werte. verändern tue ich sie nicht...

padding ist auf 1 byte, und die größe der struktur stimmt auch
(19 DWORDs).

da ich im kernelmode bin, haben USERESP und SS keine werte vom
int bekommen...

erik.vikinger

  • Beiträge: 1 277
    • Profil anzeigen
Gespeichert
« Antwort #5 am: 08. April 2010, 18:04 »
Hallo,


außerdem ist dieser teil des codes nicht von dem compilerwechsel betroffen (vor dem wechsel funktionierte das ganze komischerweise).
Vergleiche doch mal den Output der verschiedenen Compiler, vielleicht findest Du dort den Fehler.
Für die Interrupt-Stubs würde ich persönlich auch nicht den Inline-Assembler nehmen sondern das in eine separate Assembler-Datei packen.

da ich im kernelmode bin, haben USERESP und SS keine werte vom int bekommen...
Ich glaub zwar nicht das es was bringt aber vielleicht setzt Du die Werte mal manuell auf was gescheites.

Dein Problem erscheint mir insgesamt recht merkwürdig. Wenn Du meinst das es mit einem Compilerwechsel angefangen hat dann solltest Du dort suchen. Eventuell verwaltet der neue Compiler die Stack-Frames etwas anders, schau Dir mal passendes Disassembalt an. Der Funktionstyp ist eindeutig auf cdecl gesetzt?


Grüße
Erik
Reality is that which, when you stop believing in it, doesn't go away.

Gotbread

  • Beiträge: 5
    • Profil anzeigen
Gespeichert
« Antwort #6 am: 08. April 2010, 18:37 »
die funktion ist zu 100% __cdecl, und die interrupt-stubs sind alles
__declspec(naked) funktionen (es wird also kein pro oder epilog erzeugt).

ich kann ja in bochs durch meinen handler (in asm) laufen, mir fällt da
nix besonderes auf.

edit:

sogar wenn ich sofort "IRET" ausfürhe, fliegt die exception. hier mal ein trace:
http://codepad.org/68iq7TpK
« Letzte Änderung: 08. April 2010, 18:48 von Gotbread »

erik.vikinger

  • Beiträge: 1 277
    • Profil anzeigen
Gespeichert
« Antwort #7 am: 08. April 2010, 19:06 »
Hallo,


sogar wenn ich sofort "IRET" ausfürhe, fliegt die exception. hier mal ein trace:
beiss:s=0x0010, dl=0x0000ffff, dh=0x00cf9300, valid=31finde ich die Werte hinter 'dl', 'dh' und 'valid' merkwürdig. Sind Deine Segmente nur 64 kBytes groß? Für was steht die 31 bei valid?
Sorry, falls meine Fragen blöd sind, ich kenne mich mit Bochs gar nicht aus.


Grüße
Erik
Reality is that which, when you stop believing in it, doesn't go away.

Gotbread

  • Beiträge: 5
    • Profil anzeigen
Gespeichert
« Antwort #8 am: 09. April 2010, 00:00 »
fehler gefunden  :|

statt "IRET" muss es "IRETD" heißen, damit er auch 32Bit werte nimmt...
selbes problem wie beim PUSHA/PUSHAD.

der erste assembler (NASM) hat dabei keinen unterschied gemacht.

jetzt läuft es wie es soll :)

trotzdem danke an euch ;)

 

Einloggen