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.