Autor Thema: CPU-Reset (Triple Fault) bei Interrupts  (Gelesen 2825 mal)

tiger717

  • Beiträge: 84
    • Profil anzeigen
Gespeichert
« am: 10. June 2012, 17:44 »
Hallo liebe Lowlevel-Community!

Bereits vor etwa einem Jahr habe ich mich mit ein bisschen mit OS-Dev beschäftigt und hier auch schon eine Frage gestellt. Ein wenig später hab ich allerdings meine Ubuntu-Installation geschrottet. Neulich habe ich mir dann wieder Ubuntu eingerichtet und wieder begonnen.  :-D

Wie auch immer, ich stehe gerade vor einem (wahrscheinlich meiner unendlichen Dummheit zuzuschreibendem) Problem:
Nach Aktivierung der Interrupts mittels "sti" gibts einen Triple Fault -> CPU Reset. Leider lässt sich der Fehler schwer finden, denn gdb braucht offensichtlich Interrupts für Breakpoints.

Als Codebasis habe ich eine Kombination von Lowlevel-Wiki-Tutorials, OS-Dev.org-Wiki-Tutorials und was man noch alles so im Netz findet.

%macro idtentry 1

dw ((%1 - $$) + SECTIONBASE) & 0xFFFF
dw 0x08
db 0x0
db 0b10001110
dw (((%1 - $$) + SECTIONBASE) >> 16) & 0xFFFF

%endmacro

%macro handler 1
extern handler_%1
int_stub_%1:
push dword 0x0
push dword %1

push ebp
push edi
push esi
push edx
push ecx
push edx

push esp

call handler_%1

add esp, 0x4

pop eax
pop ebx
pop ecx
pop edx
pop esi
pop edi
pop ebp

add esp, 0x8

iret
%endmacro

%macro handler_errorcode 1
extern handler_%1
int_stub_%1:
push dword %1

push ebp
push edi
push esi
push edx
push ecx
push edx

push esp

call handler_%1

add esp, 0x4

pop eax
pop ebx
pop ecx
pop edx
pop esi
pop edi
pop ebp

add esp, 0x8

iret
%endmacro

%macro handler_irq_low 1
extern handler_%1
int_stub_%1:
push dword 0x0
push dword %1

push ebp
push edi
push esi
push edx
push ecx
push edx

push esp

call handler_%1

add esp, 0x4

pop eax
pop ebx
pop ecx
pop edx
pop esi
pop edi
pop ebp

add esp, 0x8

mov al, 0x20
out 0x20, al

iret
%endmacro

%macro handler_irq_high 1
extern handler_%1
int_stub_%1:
push dword 0x0
push dword %1

push ebp
push edi
push esi
push edx
push ecx
push edx

push esp

call handler_%1

add esp, 0x4

pop eax
pop ebx
pop ecx
pop edx
pop esi
pop edi
pop ebp

add esp, 0x8

mov al, 0x20
out 0xA0, al
out 0x20, al

iret
%endmacro

global loader
 
extern main

MODULEALIGN equ  1<<0                   ; align loaded modules on page boundaries
MEMINFO     equ  1<<1                   ; provide memory map
FLAGS       equ  MODULEALIGN | MEMINFO  ; this is the Multiboot 'flag' field
MAGIC       equ    0x1BADB002           ; magic number
CHECKSUM    equ -(MAGIC + FLAGS)        ; checksum

SECTIONBASE equ 0x0100000

section .text
align 4
    dd MAGIC
    dd FLAGS
    dd CHECKSUM
 
STACKSIZE equ 0x4000                    ; 16k
 
loader:
cli

mov esp, stack + STACKSIZE

lgdt [cs:GDTR]

jmp 0x08:shortjmp
shortjmp:

mov ax, 0x10
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ss, ax

lidt [cs:IDTR]

mov al, 0x11
out 0x20, al
call io_wait
mov al, 0x20
out 0x21, al
call io_wait
mov al, 0x04
out 0x21, al
call io_wait
mov al, 0x01
out 0x21, al
call io_wait

mov al, 0x11
out 0xa0, al
call io_wait
mov al, 0x28
out 0xa1, al
call io_wait
mov al, 0x02
out 0xa1, al
call io_wait
mov al, 0x01
out 0xa1, al
call io_wait

xor al, al
out 0x20, al
call io_wait
out 0xa0, al
call io_wait

sti

int 0x30 ; als Testinterrupt mit Textausgabe. Triple Fault gibts aber auch so (PIT)

    ;push eax                            ; Multiboot magic number
;push ebx                            ; Multiboot info structure

call main

hang:
hlt
jmp hang

io_wait:
jmp dly
dly: ret

handler 0
;...
handler 48

GDTR:
dw GDT_END-GDT-1
dd GDT
GDT:
dd 0x0, 0x0
db 0xFF, 0xFF, 0x0, 0x0, 0x0, 0b10011010, 0b11001111, 0x0
db 0xFF, 0xFF, 0x0, 0x0, 0x0, 0b10010010, 0b11001111, 0x0
;db 0xFF, 0xFF, 0x0, 0x0, 0x0, 0b11111010, 0b11001111, 0x0 ; zu Fehlersuchzwecken auskommentiert
;db 0xFF, 0xFF, 0x0, 0x0, 0x0, 0b11110010, 0b11001111, 0x0
GDT_END:

IDTR:
dw 49 * 8 - 1
dd IDT
IDT:
idtentry int_stub_0
;...
idtentry int_stub_48

section .bss
align 4
stack:
    resb STACKSIZE


Außerdem noch: kernel.c (mit void main, nichts besonderes) und handler.c (mit Handlern, derzeit ohne wirklichen Inhalt)
Wichtig! Verwendet wird SYSLINUX, nicht GRUB!  :wink:
Kompiliert mit
CC =gcc
CFLAGS =-Wall -Werror -Wextra -Wshadow -Wconversion -Wunreachable-code -Werror-implicit-function-declaration -Wuninitialized -nostdlib -nostartfiles -nodefaultlibs -ffreestanding -fno-builtin -m32 -g3
LDFLAGS =-m elf_i386
AS =nasm
LD =ld
 
all:kernel.bin
 
kernel.bin:
$(AS) -f elf -o loader.elf loader.S
$(CC) $(CFLAGS) -o kernel.elf -c kernel.c
$(CC) $(CFLAGS) -o handler.elf -c handler.c
$(LD) $(LDFLAGS) -T linker.ld -o kernel.bin loader.elf kernel.elf handler.elf
objcopy --only-keep-debug kernel.elf kernel.sym
objcopy --strip-debug kernel.elf
[...]

Der Linkerscript ist "Standard".

Zum Ausführen nehme ich eine Variante des folgenden Scriptes: wiki.osdev.org/Bare_Bones

Den Reset gibt es nur bei eingeschalteten Interrupts. Ohne wird void main ordnungsgemäß aus geführt.

Hoffe, ich habe mein Problem verständlich erklärt und keine Information vergessen.  :roll:

Jidder

  • Administrator
  • Beiträge: 1 625
    • Profil anzeigen
Gespeichert
« Antwort #1 am: 10. June 2012, 18:09 »
Den Reset gibt es nur bei eingeschalteten Interrupts. Ohne wird void main ordnungsgemäß aus geführt.
Heißt das, dass int 0x30 funktioniert und im richtigen Interrupthandler landet?

Als Codebasis habe ich eine Kombination von Lowlevel-Wiki-Tutorials, OS-Dev.org-Wiki-Tutorials und was man noch alles so im Netz findet.

%macro idtentry 1

dw ((%1 - $$) + SECTIONBASE) & 0xFFFF
dw 0x08
db 0x0
db 0b10001110
dw (((%1 - $$) + SECTIONBASE) >> 16) & 0xFFFF

%endmacro

Wo hast du das Makro her? Ich bezweifle nämlich, dass das funktioniert. Der Wert von SECTIONBASE müsste der Wert sein, an dem die .text-Sektion von loader.elf landet. Das ist nicht 0x100000, sondern sehr wahrscheinlich ein etwas höherer Wert, weil der ELF-Header noch davor kommt.

Ich würde dir dazu raten die IDT zur Laufzeit zu befüllen. (Oder zu überprüfen, ob diese vor dem Aktivieren der Interrupts tatsächlich korrekt befüllt ist.)
« Letzte Änderung: 10. June 2012, 18:22 von Jidder »
Dieser Text wird unter jedem Beitrag angezeigt.

tiger717

  • Beiträge: 84
    • Profil anzeigen
Gespeichert
« Antwort #2 am: 10. June 2012, 18:50 »
Den Reset gibt es nur bei eingeschalteten Interrupts. Ohne wird void main ordnungsgemäß aus geführt.
Heißt das, dass int 0x30 funktioniert und im richtigen Interrupthandler landet?
Nein.
Interruptflag nicht gesetzt -> void main wird ausgeführt (Text wird angezeigt)
Interruptflag gesetzt -> Triple Fault (egal ob mit oder ohne int 0x30)
Zitat
Als Codebasis habe ich eine Kombination von Lowlevel-Wiki-Tutorials, OS-Dev.org-Wiki-Tutorials und was man noch alles so im Netz findet.

%macro idtentry 1

dw ((%1 - $$) + SECTIONBASE) & 0xFFFF
dw 0x08
db 0x0
db 0b10001110
dw (((%1 - $$) + SECTIONBASE) >> 16) & 0xFFFF

%endmacro

Wo hast du das Makro her?
Eigenkreation. :-D Wegen dem SECTIONBASE, siehe auch z.B. hier
Zitat
Ich bezweifle nämlich, dass das funktioniert. Der Wert von SECTIONBASE müsste der Wert sein, an dem die .text-Sektion von loader.elf landet. Das ist nicht 0x100000, sondern sehr wahrscheinlich ein etwas höherer Wert, weil der ELF-Header noch davor kommt.

Ich würde dir dazu raten die IDT zur Laufzeit zu befüllen. (Oder zu überprüfen, ob diese vor dem Aktivieren der Interrupts tatsächlich korrekt befüllt ist.)
Hmm. Schade, wollte eigentlich gerne den IDT schon vorher "fest" haben, aber na ja. Ich höre auf den Rat des Meisters. :-D

tiger717

  • Beiträge: 84
    • Profil anzeigen
Gespeichert
« Antwort #3 am: 10. June 2012, 21:19 »
Hmm, Code entsprechend geändert. Triple fault leider immer noch da.  :-(

QEMU-Logfile:
Triple fault
CPU Reset (CPU 0)
EAX=ffffffff EBX=0000000d ECX=0000000d EDX=ffffff2d
ESI=0000000d EDI=0000000d EBP=00101218 ESP=00101000
EIP=001066a0 EFL=00000086 [--S--P-] CPL=0 II=0 A20=1 SMM=0 HLT=0
ES =0010 00000000 ffffffff 00cf9300 DPL=0 DS   [-WA]
CS =0008 00000000 ffffffff 00cf9a00 DPL=0 CS32 [-R-]
SS =0010 00000000 ffffffff 00cf9300 DPL=0 DS   [-WA]
DS =0010 00000000 ffffffff 00cf9300 DPL=0 DS   [-WA]
FS =0010 00000000 ffffffff 00cf9300 DPL=0 DS   [-WA]
GS =0010 00000000 ffffffff 00cf9300 DPL=0 DS   [-WA]
LDT=0000 00000000 00000000 00008200 DPL=0 LDT
TR =0008 00000580 00000067 00008900 DPL=0 TSS32-avl
GDT=     0010100d 00000017
IDT=     001066c4 00000187
CR0=00000011 CR2=00000000 CR3=00000000 CR4=00000000
DR0=00000000 DR1=00000000 DR2=00000000 DR3=00000000
DR6=ffff0ff0 DR7=00000400
CCS=ffffffff CCD=ffffffff CCO=ADDB   
EFER=0000000000000000
FCW=037f FSW=0000 [ST=0] FTW=00 MXCSR=00001f80
FPR0=0000000000000000 0000 FPR1=0000000000000000 0000
FPR2=0000000000000000 0000 FPR3=0000000000000000 0000
FPR4=0000000000000000 0000 FPR5=0000000000000000 0000
FPR6=0000000000000000 0000 FPR7=0000000000000000 0000
XMM00=00000000000000000000000000000000 XMM01=00000000000000000000000000000000
XMM02=00000000000000000000000000000000 XMM03=00000000000000000000000000000000
XMM04=00000000000000000000000000000000 XMM05=00000000000000000000000000000000
XMM06=00000000000000000000000000000000 XMM07=00000000000000000000000000000000

Mein Code:
lea ax, [%1]
and ax, 0xFFFF
mov word [edi], ax
mov word [edi+2], 0x08
mov byte [edi+4], 0x0
mov byte [edi+5], 0b10001110
lea eax, [%1]
shr eax, 0x10
and eax, 0xFFFF
mov word [edi+6], ax
add edi, 0x08

Memory-Dump. Einträge sind eigentlich gut zu sehen...
(qemu) xp /20xh 0x01066c4
00000000001066c4: 0x0a06 0x0008 0x8e00 0x0010 0x0a24 0x0008 0x8e00 0x0010
00000000001066d4: 0x0a42 0x0008 0x8e00 0x0010 0x0a60 0x0008 0x8e00 0x0010
00000000001066e4: 0x0a7e 0x0008 0x8e00 0x0010

Wahrscheinlich irgendein dummer Fehler, weil ich den Code so schnell zusammengeschrieben habe...
« Letzte Änderung: 10. June 2012, 21:22 von tiger717 »

Dimension

  • Beiträge: 155
    • Profil anzeigen
Gespeichert
« Antwort #4 am: 11. June 2012, 09:16 »
Kann auf meinem Android-Device den Code jetzt nicht ganz nachvolliehen, aber Triple Fault kommt, wenn du kein ISR für INT 0x08 (Double Fault) eingerichtet hast. Nach dem STI kommt glaub als erstes ein Timer-Interrupt, wenn du dafür kein ISR hast -> Double Fault -> Triple Fault. Weiss jetz nicht ganz, was du mit gdb genau debuggen willst, wenn es offensichtlich beim STI crasht.

kevin

  • Administrator
  • Beiträge: 2 767
    • Profil anzeigen
Gespeichert
« Antwort #5 am: 11. June 2012, 09:30 »
QEMU-Logfile: [...]
Die zwei bis drei Einträge davor sind eigentlich interessanter. Ein Triple Fault entsteht ja nicht aus dem Nichts, sondern wird durch irgendeine Exception, die nicht behandelt werden kann, ausgelöst.

Auf jeden Fall ist entweder deine IDT oder deine GDT kaputt. Größere Mengen noch dazu unkommentierten Assemblercode lese ich prinzipiell nicht durch, dafür ist mir meine Zeit zu schade.

Nach dem STI kommt glaub als erstes ein Timer-Interrupt, wenn du dafür kein ISR hast -> Double Fault -> Triple Fault.
Nicht ganz richtig: Wenn kein gültiger IDT-Eintrag für den IRQ da ist, dann gibt es erstmal einen #GP, und erst dass der auch nicht da ist, gibt einen #DF.
Thou shalt not follow the NULL pointer, for chaos and madness await thee at its end.

tiger717

  • Beiträge: 84
    • Profil anzeigen
Gespeichert
« Antwort #6 am: 11. June 2012, 18:51 »
   push ebp
   push edi
   push esi
   push edx
   push ecx
   push edx


   push esp

   call handler_%1

   add esp, 0x4

   pop eax
   pop ebx
   pop ecx
   pop edx

   pop esi
   pop edi
   pop ebp

OMG. Peinlich, peinlich...

 

Einloggen