Autor Thema: Exception wird nicht abgefangen  (Gelesen 6850 mal)

TheThing

  • Beiträge: 105
    • Profil anzeigen
Gespeichert
« am: 29. January 2009, 12:33 »
Hi,
ich schreibe meinen Kernel in FreeBASIC. Jetzt bin ich an der Stelle, an der es sinnvoll wird interrupts nutzen zu können. Daher hab ich mir Routinen geschrieben, die eine IDT erstellen und laden. Testen wollte ich das ganze folgendermaßen:
Ich habe eine funktion als interrupt 0 in die IDT eingetragen, und der Kernel ruft danach (natürlich nachdem die IDT geladen wurde) int 0 auf. Er sollte dann eigentlich einen Text ausgeben, aber alles was er tut, ist abstürzen.
Ich hab mehrmals rumprobiert, aber ich krieg es einfach nicht hin. Ich bin kurz vorm verzweifeln.
Obwohl FreeBASIC eine ungewöhnliche Sprache für einen Kernel ist, hoffe ich das ihr mir helfen könnt.

Die letzte funktionierende Version meines Kernels gibt es auf http://frostkernel.sourceforge.net

Die Version, die Proleme macht, gibts hier (source+image): http://darkinsanity.freehoster.ch/FROST_debug.zip

Die Logfile von Bochs ist hier: http://darkinsanity.freehoster.ch/bochs.log

kevin

  • Administrator
  • Beiträge: 2 767
    • Profil anzeigen
Gespeichert
« Antwort #1 am: 29. January 2009, 12:58 »
Dreh mal das bochs-Loglevel ein bißchen weiter hoch (auf debug), dann wird er dir auch sagen, was genau zu diesem Triple Fault geführt hat. Aber mit einiger Sicherheit kann man schonmal sagen, daß entweder die GDT oder die IDT in irgendeiner Weise kaputt ist.
Thou shalt not follow the NULL pointer, for chaos and madness await thee at its end.

TheThing

  • Beiträge: 105
    • Profil anzeigen
Gespeichert
« Antwort #2 am: 29. January 2009, 18:52 »
gut zu wissen das man das loglevel höher drehen kann  :-)
Ich benutze erst seit 2 Tagen Bochs, vorher hatte ich qemu, aber das will unter Windows (was leider mein Haupt-OS ist) auf ein Linux Verzeichnis zugreifen wenn es ein logfile schreiben soll.
Das es auch die GDT sein könnte, daran hab ich noch gar nicht gedacht...
Ich werd heute Abend mal ein bisschen an Bochs rumprobieren.

kevin

  • Administrator
  • Beiträge: 2 767
    • Profil anzeigen
Gespeichert
« Antwort #3 am: 29. January 2009, 20:01 »
Du kannst den Dateinamen der qemu-Logdatei im Monitor ändern. Aber auf der Kommanozeile anscheinend nicht, seltsam. Wäre fast mal einen Patch wert...
Thou shalt not follow the NULL pointer, for chaos and madness await thee at its end.

TheThing

  • Beiträge: 105
    • Profil anzeigen
Gespeichert
« Antwort #4 am: 04. February 2009, 12:35 »
wie komm ich denn dann aus dem monitor wieder raus?
hier ist die komplette logfile von Bochs: http://darkinsanity.freehoster.ch/bochs.7z

der kritische Teil scheint der zu sein:
00377691208d[CPU0 ] interrupt(): vector = 0, INT = 1, EXT = 0
00377691208d[CPU0 ] interrupt(): gate descriptor is not valid sys seg (vector=0x00)
00377691208d[CPU0 ] exception(0x0d): error_code=0002
00377691208d[CPU0 ] interrupt(): vector = 13, INT = 0, EXT = 1
00377691208d[CPU0 ] interrupt(): gate descriptor is not valid sys seg (vector=0x0d)
00377691208d[CPU0 ] exception(0x0d): error_code=006a
00377691208d[CPU0 ] exception(0x08): error_code=0000
00377691208d[CPU0 ] interrupt(): vector = 8, INT = 0, EXT = 1
00377691208d[CPU0 ] interrupt(): gate descriptor is not valid sys seg (vector=0x08)
00377691208d[CPU0 ] exception(0x0d): error_code=0042
00377691208i[CPU0 ] CPU is in protected mode (active)
00377691208i[CPU0 ] CS.d_b = 32 bit
00377691208i[CPU0 ] SS.d_b = 32 bit
00377691208i[CPU0 ] EFER   = 0x00000000
00377691208i[CPU0 ] | RAX=0000000000000960  RBX=000000000002c3ac
00377691208i[CPU0 ] | RCX=00000000000000a0  RDX=0000000000000000
00377691208i[CPU0 ] | RSP=0000000000067e9c  RBP=0000000000067ecc
00377691208i[CPU0 ] | RSI=000000000002c9e4  RDI=000000000002c9e9
00377691208i[CPU0 ] |  R8=0000000000000000   R9=0000000000000000
00377691208i[CPU0 ] | R10=0000000000000000  R11=0000000000000000
00377691208i[CPU0 ] | R12=0000000000000000  R13=0000000000000000
00377691208i[CPU0 ] | R14=0000000000000000  R15=0000000000000000
00377691208i[CPU0 ] | IOPL=0 id vip vif ac vm RF nt of df if tf sf zf af PF cf
00377691208i[CPU0 ] | SEG selector     base    limit G D
00377691208i[CPU0 ] | SEG sltr(index|ti|rpl)     base    limit G D
00377691208i[CPU0 ] |  CS:0008( 0001| 0|  0) 00000000 000fffff 1 1
00377691208i[CPU0 ] |  DS:0010( 0002| 0|  0) 00000000 000fffff 1 1
00377691208i[CPU0 ] |  SS:0010( 0002| 0|  0) 00000000 000fffff 1 1
00377691208i[CPU0 ] |  ES:0010( 0002| 0|  0) 00000000 000fffff 1 1
00377691208i[CPU0 ] |  FS:0010( 0002| 0|  0) 00000000 000fffff 1 1
00377691208i[CPU0 ] |  GS:0010( 0002| 0|  0) 00000000 000fffff 1 1
00377691208i[CPU0 ] |  MSR_FS_BASE:0000000000000000
00377691208i[CPU0 ] |  MSR_GS_BASE:0000000000000000
00377691208i[CPU0 ] | RIP=00000000001014c1 (00000000001014c1)
00377691208i[CPU0 ] | CR0=0x60000011 CR1=0x0 CR2=0x0000000000000000
00377691208i[CPU0 ] | CR3=0x00000000 CR4=0x00000000
00377691208i[CPU0 ] >> int 0x00 : CD00
00377691208d[CTRL ] searching for component 'cpu' in list 'bochs'
00377691208d[CTRL ] searching for component 'reset_on_triple_fault' in list 'cpu'
00377691208e[CPU0 ] exception(): 3rd (13) exception with no resolution, shutdown status is 00h, resetting
00377691208i[SYS  ] bx_pc_system_c::Reset(SOFTWARE) called
00377691208d[SYS  ] A20: set() = 1
00377691208i[CPU0 ] cpu software reset

bluecode

  • Beiträge: 1 391
    • Profil anzeigen
    • lightOS
Gespeichert
« Antwort #5 am: 04. February 2009, 13:07 »
wie komm ich denn dann aus dem monitor wieder raus?
Alt+Strg+1?

-edit-
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

ChristianF

  • Beiträge: 296
    • Profil anzeigen
    • DeutschOS - Betriebssystem Projekt
Gespeichert
« Antwort #6 am: 05. February 2009, 08:48 »
So...
ich habe mir das mal angeschaut. Es ist vielleicht so, dass ich C/C++ verwende, aber Basic verstehe ich auch, zumindest ein wenig... ;)
 
Dabei ist mir aufgefallen, dass du die IDT zwar lädst (mittels LoadIDT(...)) allerdings rufst du "IDTInit()" nicht auf (zumindest habe ich dies nirgends gesehen).
 
Des Weiteren rufst du in der Datei FROSTmain.bas "int 0" auf. Der Interrupt 0 so wie du ihn aufrufst, muss allerdings erst zugeordnet werden, denn die Interrupts 0 bis 31 sind reserviert und können nur vom Prozessor generiert werden.
Was ich an deiner Stelle auch noch machen würde, was ich nirgends entdeckt habe, ist die komplette IDT-Struktur auf 0 zu setzen, also z.B. so
memset(&idt, sizeof(struct idt_entry) * 256); /* Hierbei handelt es sich um C-Code */
Dies ist, was mir so aufgefallen ist. Kann auch sein, dass ich was übersehen habe oder mich irgendwo geirrt habe.
Was aus den Log-Files hervorgeht, ist halt, dass dem Interrupt 0, den man aufrufen kann, nix zugeordnet ist. Des Weiteren wird dann ein "General Protection Fault"-Interrupt generiert, der allerdings auch nicht gehandelt wird, weshalb es wieder zu einem Fehler kommt.
 
 
Gruß Christian
 
PS: Ich hoffe, das war einigermaßen verständlich.
PPS: Nach einem vom Prozessor generierten Interrupt sollte man das System unter Umständen mit einer Endlosschleife anhalten, da man nicht weiß, was danach passiert...
 
 
*EDIT*
Wenn ich jetzt nicht ganz falsch liege, sollte wenn du "int 0" ausführen möchtest, der IDT-Eintrag 48 mit einer Funktion verknüpft werden, denn 0 - 31 (einschließlich 31) werden vom Prozessor generiert und dann 32 - 47 (auch wieder einschließlich 47) werden von den IRQs belegt.
« Letzte Änderung: 05. February 2009, 10:22 von ChristianF »

Jidder

  • Administrator
  • Beiträge: 1 625
    • Profil anzeigen
Gespeichert
« Antwort #7 am: 05. February 2009, 11:01 »
Interrupt 0 ist Eintrag 0. Das stimmt schon. Man kann den PIC befehlen, die IRQs sonstwohin (z.B. 32-47) zu mappen, aber darum gehts hier ja nich ;)
Dieser Text wird unter jedem Beitrag angezeigt.

bitmaster

  • Troll
  • Beiträge: 1 138
    • Profil anzeigen
    • OS-64 = 64 Bit Operating System
Gespeichert
« Antwort #8 am: 05. February 2009, 11:08 »
*EDIT*
Wenn ich jetzt nicht ganz falsch liege, sollte wenn du "int 0" ausführen möchtest, der IDT-Eintrag 48 mit einer Funktion verknüpft werden, denn 0 - 31 (einschließlich 31) werden vom Prozessor generiert und dann 32 - 47 (auch wieder einschließlich 47) werden von den IRQs belegt.
hä? Eher nicht. int 0 ruft den Interrupt Null auf und int 0xff den Interrupt 255. Und in der IDT steht jeder Eintrag für den selbigen Interrupt. Also schaut ein int 0 nach was im Eintrag Null steht und ein int 0xff was im Eintrag 255 steht. Außerdem lässt sich der PIC umprogrammieren. Das heißt, dass die IRQs "überall" liegen können (da gibts glaube ich Einschränkungen, weiß jetzt nicht mehr genau welche, evtl. align 8 oder so). Wenn ich mich nicht irre, dann ruft ein int 0 genau das auf, was eine Division durch Null auch machen würde, nämlich eine Exception Null.

bitmaster
In the Future everyone will need OS-64!!!

ChristianF

  • Beiträge: 296
    • Profil anzeigen
    • DeutschOS - Betriebssystem Projekt
Gespeichert
« Antwort #9 am: 05. February 2009, 11:12 »
Und wieder was neues gelernt.
Ich dachte, die Interrupts 0 bis 31 werden nur vom Prozessor generiert ...
:roll:

bitmaster

  • Troll
  • Beiträge: 1 138
    • Profil anzeigen
    • OS-64 = 64 Bit Operating System
Gespeichert
« Antwort #10 am: 05. February 2009, 11:21 »
Und wieder was neues gelernt.
Ich dachte, die Interrupts 0 bis 31 werden nur vom Prozessor generiert ...
:roll:
Das war dein großer Fehler: Nicht denken, sondern nachdenken. *hust*  :lol:
In the Future everyone will need OS-64!!!

TheThing

  • Beiträge: 105
    • Profil anzeigen
Gespeichert
« Antwort #11 am: 05. February 2009, 11:58 »
ok, erstmal vielen dank für die Antworten.
das mit memset werd ich mal einbauen.
IDTinit wird in der FROSTmain.bas aufgerufen (direkt nachdem der PIC umprogrammiert wurde):
print "Initializing IDT..."
IDTinit                                                 '// Loading a ground-IDT

gibt es einen Weg wie ich überprüfen kann ob die GDT korrekt ist? Hab nämlich alle Angaben usw. beim generieren des IDT-Eintrags überprüft und die scheinen gültig zu sein, daher vermute ich das die GDT der Übeltäter ist.

MNemo

  • Beiträge: 547
    • Profil anzeigen
Gespeichert
« Antwort #12 am: 05. February 2009, 12:17 »
Zitat von: bochs
00377691208d[CPU0 ] interrupt(): vector = 0, INT = 1, EXT = 0
00377691208d[CPU0 ] interrupt(): gate descriptor is not valid sys seg (vector=0x00)
[...]
00377691208i[CPU0 ] |  CS:0008( 0001| 0|  0) 00000000 000fffff 1 1
00377691208i[CPU0 ] |  DS:0010( 0002| 0|  0) 00000000 000fffff 1 1
[...]

wie du siehst kann bochs deine gdt zumindest lesen ohne einen fehler festzustellen( sonst hätter er die Segment register nicht geladen) aber bochs sagt auch eindeutig das deine IDT fehlerhaft ist. jetzt ist nur noch die frage was genau der fehler ist. dazu guck ich mir jetzt ma deine code an.

[edit]
da hamm wirs ja schon ..
Zitat von: bochs:'info idt'
Interrupt Descriptor Table (base=0x0000000030700000, limit=2047):
bx_dbg_read_linear: physical memory read error (phy=0x30700000, lin=0x0000000030700000)
error: IDTR+8*0 points to invalid linear address 0x0000000030700000
du übergibst deiner LoadIDT offensichtlich die falsche base-address
da ich von Basic keine ahnung hab kann ich aber nicht sagen wies richtig geht

[edit]
aber ich kann dir sagen was mir so auffällt
Zitat von: FROSTmain.bas
LoadGDT (8192*8-1, cast(UINTEGER, @gdtEntries(1)))
[...]
LoadIDT (256*8-1 , cast(UINTEGER, @IDTentries(0)))
warum einmal '1' und einmal '0' sollten nicht beide gleich sein

Zitat von: LoadGDT
    address = (cast(UINTEGER, @gdt))

    ASM
        lgdt [address]
    END ASM
Zitat von: LoadIDT
    idt.size = size

    ASM
       lidt [idt]
    END ASM
auch hier sollte es keinen unterschied geben!?
« Letzte Änderung: 05. February 2009, 12:46 von MNemo »
„Wichtig ist nicht, besser zu sein als alle anderen. Wichtig ist, besser zu sein als du gestern warst!“

TheThing

  • Beiträge: 105
    • Profil anzeigen
Gespeichert
« Antwort #13 am: 06. February 2009, 12:47 »
Der erste Eintrag in der IDT ist ja 0, und bei der GDT wird von 1 gezählt, also sollte das doch stimmen, oder?

Die IDT wird so geladen (ich hab das auch so in nem C-Beispiel gesehn):
sub LoadIDT (size as USHORT, BaseAddr as UINTEGER)
    dim idt as IDTstructure
   
    idt.BaseAddr = BaseAddr
    idt.size = size
   
    ASM
       lidt [idt]
    END ASM
end sub

Ich hab mitlerweile eine neuere Version gebastelt (liegt leider daheim auf meinem PC, habs vergessen aufn USB-Stick zu ziehn und kanns deswegen nich hochladen) die die GDT so lädt wie auch die IDT.
Kann es vielleicht sein das ich die eckigen Klammern weglassen muss?

Jidder

  • Administrator
  • Beiträge: 1 625
    • Profil anzeigen
Gespeichert
« Antwort #14 am: 06. February 2009, 17:02 »
Der erste Eintrag der GDT ist nur nicht benutzbar, d.h. du kannst ein Segmentregister nicht ohne einen General Protection Fault auszulösen mit 0 (bis 7) laden. Trotzdem musst du den Eintrag in der GDT haben, und darfst ihn nicht irgendwie überspringen. Es ist egal, was da drin steht.

Zum Problem: Du lädst die Segment Register nicht neu, nach dem du LGDT ausgeführt hast, sondern es stehen noch die alten Werte von GRUB drin. Deswegen merkst du nicht, dass deine GDT auch schon falsch geladen worden ist.

Der GPF beim Interrupt entsteht dann vermutlich, weil da CS neu geladen wird.
Dieser Text wird unter jedem Beitrag angezeigt.

TheThing

  • Beiträge: 105
    • Profil anzeigen
Gespeichert
« Antwort #15 am: 12. February 2009, 12:36 »
so, hab meinen Kernel überarbeitet.
Er nutzt jetzt Interrupt 0x30. GDT & IDT werden am Anfang mit memset auf 0 gesetzt, der 0. Eintrag (mit nullen gefüllt) in der GDT wird beim laden berücksichtigt.
Leider geht der ganze Spaß immer noch nicht.
Kernel: http://darkinsanity.freehoster.ch/FROST_debug.zip
Logfile: http://darkinsanity.freehoster.ch/bochs_log.7z

PS: was muss ich denn für den GNU-assembler schreiben wenn ich die Segmentregister neu laden will?

MNemo

  • Beiträge: 547
    • Profil anzeigen
Gespeichert
« Antwort #16 am: 12. February 2009, 16:26 »
hmm...

ich glaube, dass deine deine IDTstructur-Elemente 4Byte aligned sind(in C(gcc) kannst man das mit __attribute__((packed)) verhindern).
denn laut
$ objdump -t frost.krn | grep IDTENTRIES
00113078 l     O .bss   00000800 IDTENTRIES
begin deine idt bei 0x00113078 bochs will sie aber von 0x30780000 laden. bei der gdt ist es genau das gleiche.

segmentregister kann man gennerell nicht direkt mit einem wert laden. das geht nur mit einem umweg über ein allzweckregister.
in gas sieht das dann so aus(nicht getestet):
movl $0x10, %eax
mov %ax, %ds
„Wichtig ist nicht, besser zu sein als alle anderen. Wichtig ist, besser zu sein als du gestern warst!“

TheThing

  • Beiträge: 105
    • Profil anzeigen
Gespeichert
« Antwort #17 am: 17. February 2009, 12:42 »
genau das wars!
Vielen, vielen Dank.

Ich musste nur nach den types ein "FIELD=1" einfügen, und schon hats geklappt.
Jetzt muss ich nur noch die interrupt-routinen in eine externe Datei auslagern.

 

Einloggen