Division durch Null
-
Ich habe inzwischen die IDT eingerichtet, als auch die isr- und irq-Routinen, die ihrerseits einen Handler aus einen array aufrufen, bzw. entsprechend bei den ISR-Routinen eine Fehlermeldung ausgeben. Der ISR-Handler als auch der IRQ-Handler werden immer mit einen EOI beendet. Der PIT wurde so programmiert,
daß die Interrupts von 0 bis 15 auf die IRQ nummern 32 bis 47 verschoben wurden.
die ISR-Routinen 0 bis 31 decken sich mit den Interruptnummern von 0 bis 31.// EOI if (Interruptnr>=40) { outportb(0xA0,0x20); } outportb(0x20,0x20);
Um die IRQ- und ISR- Routinen als auch deren Handler zu testen, baute ich
eine "Division durch Null" ein.Wenn ich im C++ Programm Anweisungen wie
unsigned char x=1; x=x/0;
ausführe, passiert nichts! Offenbar fängt der Compiler diese Ausnahme ab, auch wenn der gcc-Compiler nur eine Warnung ausgibt.
Wenn ich jedoch Assembleranweisungen wie
asm("mov $0x1, %ax;" "mov $0x0, %bx;" "idiv %bx");
in den C++-Code einfüge, dann entsteht eine "Division durch Null"-Ausnahme. Jedoch wird sie endlos generiert, so als würden die EOI-Anweisungen ignoriert werden.
Ich befürchte, daß bei allen 32 ISR-Routinen dieses passieren würde, wenn von der Prozessorseite die Interruptnummern von 0 bis 31 erzeugt werden.Wie kann man den int 0 wieder abstellen?
-
Offenbar fängt der Compiler diese Ausnahme ab, auch wenn der gcc-Compiler nur eine Warnung ausgibt.
Kann ich in PrettyOS mit unserem GCC-Crosscompiler nicht reproduzieren. Hier stürzt es ordnungsgemäß ab mit der Division durch 0.
Jedoch wird sie endlos generiert, so als würden die EOI-Anweisungen ignoriert werden.
Grundsätzlich sollte das nicht passieren. Was machst du denn als "EOI"? Dem PIC brauchst du kein EOI signalisieren bei Exceptions, du musst ein iret im Interrupthandler machen.
-
Bei Hardwareinterrupts wie beispielsweise beim Druck auf eine Tastaturtaste wird die Interruptleitung des Prozessors zurückgesetzt, so daß es keine unendlichen Wiederholungen gibt. Jetzt weiß ich noch nicht, ob bei einer Aussnahme ebenfalls die Interruptleitung gesetzt wird. Vielleicht gibt es ein Statusflag, welches das Auftauchen eines Traps anzeigen.
Bei mir werden alle Interrupts und Traps mit einen iret beendet.
Soweit ich weiß, werden bei iret im Gegensatz zu ret nur mehr Register vom Stack heruntergeholt und sti intern ausgeführt. Ob es ebenfalls die Interruptleitung in jedem Falle zurücksetzt, weiß ich nicht.extern _IRQ_Handler extern _ISR_Handler _isr0: ; 0 Division durch Null Ausnahme cli push dword 0 ;Fehlernummer push dword 0 ;Interruptnr jmp ISR_Routine . . . _irq0: ; cli push dword 0 push dword 0 jmp IRQ_Routine . . . ISR_Routine: push eax push ebx push ecx push edx 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 call _ISR_Handler pop esp pop gs pop fs pop es pop ds pop edi pop esi pop ebp pop edx pop ecx pop ebx pop eax add esp, 8 ; Fehlernr und Interruptnr vom Stack loeschen iret IRQ_Routine: push eax push ebx push ecx push edx 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 call _IRQ_Handler pop esp pop gs pop fs pop es pop ds pop edi pop esi pop ebp pop edx pop ecx pop ebx pop eax add esp, 8 iret
und im C-Code sieht das dann so aus:
void IRQ_Handler(uint32 esp) { CRegister *r; r=(CRegister *)esp; void (*handler)(CRegister* r); handler = (void (*)(CRegister *)) IRQ_Routinen[r->Interruptnr]; if (handler) { k_printf("Interruptnr=%d\n",r->Interruptnr); handler(r); } if (r->Interruptnr+32>=40) { outportb(0xA0,0x20); } outportb(0x20, 0x20); } void ISR_Handler(uint32 esp) { uint8 Farbe = k_getcolor(); uint8 NFarbe = Farbe; CRegister *r; r=(CRegister *)esp; if (r->Interruptnr<32) { NFarbe = NFarbe & 0xF0; NFarbe = NFarbe | 0x04; k_setcolor(NFarbe); k_printf("%s",Fehlermeldungen[r->Fehlernr]); k_setcolor(Farbe); k_printf("\n"); } else if (r->Interruptnr>0 && r->Interruptnr<MaxRoutinen) { k_printf("Interruptnr=%d",r->Interruptnr); k_setcolor(Farbe); k_printf("\n"); } if (r->Interruptnr>=40) { outportb(0xA0,0x20); } outportb(0x20,0x20); }
ich mußte, um die Struktur CRegister zu füllen, diese vielen push-Anweisungen machen. Vor dem Aufruf der Handler auch ein push esp, da ich sonst den Handler
als Handler(CRegister *r) keinen Zugriff auf die gepushten Werte in r hatte, da der C-Compiler beim Aufruf des Handlers die Parameter bei Strukturen oder Klassen nicht direkt vom Stack gelesen werden können. Aus diesen Gründen habe ich einfach eine Adresse des Stacks als Parameter übergeben. Der Aufruf erfolgt dann immer korrekt und ich kann auf die gepushten Register über die Struktur CRegister zugreifen.Interessant jedoch ist bei mir das Verhalten des gcc-Compilers
volatile int a=1; volatile int b=0; volatile int x=0; x=a/b;
Bei diesen Code erreiche ich eine Ausnahme. Lasse ich das volatile weg, passiert nichts! Das ist nicht sehr gut!
Schreibe ich jedoch unter Linux eine solche Sequenz, dann meldet er in jedem Falle (mit volatile oder ohne) etwas, und zwar einen Gleitkommafehler!Ich habe Beim Eintrag der ISR-Routinen in die IDT-Tabelle beim Flag von Bits 0 bis 3, wo der Typ festgelegt ist, das 80386 Interrupt Gate 0b1110 gesetzt.
In der Annahme, daß die ISR-Routinen eigentlich Traps und keine Interrupts sind,
habe ich nun 0b1111 für 80386 Trap Gate gesetzt.Doch auch hier wird Trap 0 unendlich wiederholt aufgerufen. Bei den IRQ's läuft alles normal
-
Bei diesen Code erreiche ich eine Ausnahme. Lasse ich das volatile weg, passiert nichts! Das ist nicht sehr gut!
Schreibe ich jedoch unter Linux eine solche Sequenz, dann meldet er in jedem Falle (mit volatile oder ohne) etwas, und zwar einen Gleitkommafehler!Möglicherweise macht GCC, wenn aktiviert, da írgendwie SSE draus.
Soweit ich weiß, werden bei iret im Gegensatz zu ret nur mehr Register vom Stack heruntergeholt und sti intern ausgeführt. Ob es ebenfalls die Interruptleitung in jedem Falle zurücksetzt, weiß ich nicht.
Bei einem Softwareinterrupt sollte die Interruptleitung des Prozessors nicht betroffen sein. Bin mir aber nicht völlig sicher; habe meine Unterlagen nicht zur Hand.
Das outportb(0x20,0x20); kannst du dir bei ISRs definitiv sparen. Der PIC hat mit ISRs nichts zu schaffen.Ich habe Beim Eintrag der ISR-Routinen in die IDT-Tabelle beim Flag von Bits 0 bis 3, wo der Typ festgelegt ist, das 80386 Interrupt Gate 0b1110 gesetzt.
In der Annahme, daß die ISR-Routinen eigentlich Traps und keine Interrupts sind,
habe ich nun 0b1111 für 80386 Trap Gate gesetzt.Die IDT von PrettyOS enthält keine Trap-Gates, soweit ich sehe.
Zu deinem eigentlichen Problem habe ich folgende Vermutung: Bei einer Exception zeigt EIP weiterhin auf die Instruktion, die den Fehler verursacht hat. Das ist auch durchaus sinnvoll, wenn z.B. geswappte Pages dann im Handler nachgeladen werden, und danach dann der Code erfolgreich ausgeführt werden kann. Ähnliches gibts im Zusammenhang mit der FPU. Im konkreten Fall heißt es aber: Es wird wieder die Division durch 0 ausgeführt. D.h., du müsstest wohl den schuldigen Prozess beenden, oder manuell über den Befehl hinweg springen.
-
Ich bin mit der Programmierung noch nicht so weit, daß ich schon USER-Prozesse erzeugen kann, und es macht offenbar kein Sinn, im Kernel-Mode Traps auszulösen. Das letztere scheint wohl zu sein, daß der EIP wohl noch auf dieser Instruktion steht. Gewöhnlicherweise werden USER-Prozesse bei einer Ausnahme beendet.