Autor Thema: Keine Interrupts im Ring 3  (Gelesen 10418 mal)

Roadrunner

  • Beiträge: 33
    • Profil anzeigen
Gespeichert
« am: 01. November 2012, 12:39 »
Hallo,
ich wollte jetzt endlich mal die Tasks, die nicht dem Kernel angehören, in den Ring 3 verschieben. Das hat so auch funktioniert, allerdings kann ich jetzt keine Interrupts mehr aufrufen. Sobald ich in dem entsprechenden Task versuche, ein int x auszuführen, stürzt das ganze System ab - QEMU schaltet sich einfach ab bzw. bootet neu. Das passiert auch beim Timer-Interrupt, den ich gar nicht selbst aufrufe. In meiner IDT sind bei allen Interrupts, die ich aufrufen will, Aufrufe aus Ring 3 erlaubt (die Flags stehen auf 0xEE). Ich habe mittlerweile auch herausgefunden, dass der Interrupthandler überhaupt nicht aufgerufen wird - nicht mal ein General Protection Fault. Ansonsten entspricht der Code, zumindest der fürs Multitasking, größtenteils dem im Tutorial.

Irgendwelche Ideen?
Exception 0x1A: Insufficient user-IQ

Jidder

  • Administrator
  • Beiträge: 1 625
    • Profil anzeigen
Gespeichert
« Antwort #1 am: 01. November 2012, 12:45 »
Führe mal qemu mit dem Parameter -d int aus. Dann erstellt schreibt er in die Datei /tmp/qemu.log (oder einfach qemu.log) die Interrupts, die aufgetreten sind. Jeder Abschnitt in der Datei entspricht dem CPU-Zustand bei einem Interrupt. Die Interruptnummer steht hinter dem v=. Da kannst du dir mal die letzten 3-4 Interrupts anschauen, und gucken was genau schief läuft (welche Exception, etc.) Oder das Log auf einem pastebin hochladen und hier verlinken.
Dieser Text wird unter jedem Beitrag angezeigt.

Roadrunner

  • Beiträge: 33
    • Profil anzeigen
Gespeichert
« Antwort #2 am: 01. November 2012, 13:37 »
Er sagt folgendes:

0: v=20
1: v=20
2: v=0a
3: v=08

Wenn diese Darstellung hexadezimal ist, dann stimmt zumindest der erste Interrupt, da der Timer ja, wie im Tutorial, auf Nummer 0x20 bzw. 32 liegt.
Exception 0x1A: Insufficient user-IQ

Jidder

  • Administrator
  • Beiträge: 1 625
    • Profil anzeigen
Gespeichert
« Antwort #3 am: 01. November 2012, 14:04 »
Und das 0a ist ein Invalid TSS Fault. Du hast also das TSS nicht richtig eingerichtet. Wie das geht wird am Ende von http://www.lowlevel.eu/wiki/Teil_6_-_Multitasking erklärt.
Dieser Text wird unter jedem Beitrag angezeigt.

Roadrunner

  • Beiträge: 33
    • Profil anzeigen
Gespeichert
« Antwort #4 am: 01. November 2012, 14:39 »
Oh. Danke. Jetzt funktioniert das ganze aber leider immer noch nicht so ganz: Nach 142 Durchgängen (laut QEMU) tritt wieder ein TSS-Fault auf. Woran kann das liegen?
Exception 0x1A: Insufficient user-IQ

Jidder

  • Administrator
  • Beiträge: 1 625
    • Profil anzeigen
Gespeichert
« Antwort #5 am: 01. November 2012, 17:08 »
An fehlerhaftem Code.

Zeig mal das log.
Dieser Text wird unter jedem Beitrag angezeigt.

Roadrunner

  • Beiträge: 33
    • Profil anzeigen
Gespeichert
« Antwort #6 am: 01. November 2012, 17:14 »
   141: v=20 e=0000 i=1 cpl=3 IP=001b:000020d0 pc=000020d0 SP=0023:00004b7e EAX=00000000
EAX=00000000 EBX=00000000 ECX=00004a3e EDX=000000f0
ESI=00000000 EDI=00000000 EBP=00005a84 ESP=00004b7e
EIP=000020d0 EFL=00000202 [-------] CPL=3 II=0 A20=1 SMM=0 HLT=0
ES =0023 00000000 ffffffff 00dff300 DPL=3 DS   [-WA]
CS =001b 00000000 ffffffff 00dffa00 DPL=3 CS32 [-R-]
SS =0023 00000000 ffffffff 00dff300 DPL=3 DS   [-WA]
DS =0023 00000000 ffffffff 00dff300 DPL=3 DS   [-WA]
FS =0000 00000000 00000000 00000000
GS =0000 00000000 00000000 00000000
LDT=0000 00000000 0000ffff 00008200 DPL=0 LDT
TR =0028 00004a40 00004aa8 0000e900 DPL=3 TSS32-avl
GDT=     00008ac0 0000002f
IDT=     000027ff 0000018f
CR0=00000011 CR2=00000000 CR3=00000000 CR4=00000000
DR0=00000000 DR1=00000000 DR2=00000000 DR3=00000000
DR6=ffff0ff0 DR7=00000400
CCS=00000001 CCD=00000000 CCO=LOGICB 
EFER=0000000000000000
check_exception old: 0xffffffff new 0xa
   142: v=0a e=0000 i=0 cpl=3 IP=001b:000020d0 pc=000020d0 SP=0023:00004b7e EAX=00000000
EAX=00000000 EBX=00000000 ECX=00004a3e EDX=000000f0
ESI=00000000 EDI=00000000 EBP=00005a84 ESP=00004b7e
EIP=000020d0 EFL=00000202 [-------] CPL=3 II=0 A20=1 SMM=0 HLT=0
ES =0023 00000000 ffffffff 00dff300 DPL=3 DS   [-WA]
CS =001b 00000000 ffffffff 00dffa00 DPL=3 CS32 [-R-]
SS =0023 00000000 ffffffff 00dff300 DPL=3 DS   [-WA]
DS =0023 00000000 ffffffff 00dff300 DPL=3 DS   [-WA]
FS =0000 00000000 00000000 00000000
GS =0000 00000000 00000000 00000000
LDT=0000 00000000 0000ffff 00008200 DPL=0 LDT
TR =0028 00004a40 00004aa8 0000e900 DPL=3 TSS32-avl
GDT=     00008ac0 0000002f
IDT=     000027ff 0000018f
CR0=00000011 CR2=00000000 CR3=00000000 CR4=00000000
DR0=00000000 DR1=00000000 DR2=00000000 DR3=00000000
DR6=ffff0ff0 DR7=00000400
CCS=00000001 CCD=00000000 CCO=LOGICB 
EFER=0000000000000000
check_exception old: 0xa new 0xa
   143: v=08 e=0000 i=0 cpl=3 IP=001b:000020d0 pc=000020d0 SP=0023:00004b7e EAX=00000000
EAX=00000000 EBX=00000000 ECX=00004a3e EDX=000000f0
ESI=00000000 EDI=00000000 EBP=00005a84 ESP=00004b7e
EIP=000020d0 EFL=00000202 [-------] CPL=3 II=0 A20=1 SMM=0 HLT=0
ES =0023 00000000 ffffffff 00dff300 DPL=3 DS   [-WA]
CS =001b 00000000 ffffffff 00dffa00 DPL=3 CS32 [-R-]
SS =0023 00000000 ffffffff 00dff300 DPL=3 DS   [-WA]
DS =0023 00000000 ffffffff 00dff300 DPL=3 DS   [-WA]
FS =0000 00000000 00000000 00000000
GS =0000 00000000 00000000 00000000
LDT=0000 00000000 0000ffff 00008200 DPL=0 LDT
TR =0028 00004a40 00004aa8 0000e900 DPL=3 TSS32-avl
GDT=     00008ac0 0000002f
IDT=     000027ff 0000018f
CR0=00000011 CR2=00000000 CR3=00000000 CR4=00000000
DR0=00000000 DR1=00000000 DR2=00000000 DR3=00000000
DR6=ffff0ff0 DR7=00000400
CCS=00000001 CCD=00000000 CCO=LOGICB 
EFER=0000000000000000
check_exception old: 0x8 new 0xa

Vor dem Eintrag 141 stehen nur die Timer-Interrupts (also entsprechend dem Eintrag 141).
Exception 0x1A: Insufficient user-IQ

Jidder

  • Administrator
  • Beiträge: 1 625
    • Profil anzeigen
Gespeichert
« Antwort #7 am: 01. November 2012, 17:24 »
Sind die Werte EIP=0x20d0, ESP=0x4b7e und TSS Basis=0x4a40 plausibel? (Das heißt, hast du die dahin gelegt?) Besonders die TSS Basis ist ziemlich niedrig, es sei denn du nutzt nicht Multiboot. Auffällig ist außerdem, dass EBP und ESP weit auseinander liegen.

Kann es sein, dass irgendwas den GDT-Eintrag überschreibt? Das TSS Limit 0x4aa8 ist nämlich eigentlich zu groß. Und überlappt sich mit dem Stack.
« Letzte Änderung: 01. November 2012, 17:26 von Jidder »
Dieser Text wird unter jedem Beitrag angezeigt.

Roadrunner

  • Beiträge: 33
    • Profil anzeigen
Gespeichert
« Antwort #8 am: 01. November 2012, 17:38 »
Die Werte könnten schon so stimmen, weil ich noch keinen Bootloader verwende, sondern QEMU nutze (qemu -kernel kernel). Und wie kann ich herausfinden, ob der GDT-Eintrag überschrieben wird?
Exception 0x1A: Insufficient user-IQ

Jidder

  • Administrator
  • Beiträge: 1 625
    • Profil anzeigen
Gespeichert
« Antwort #9 am: 01. November 2012, 17:47 »
Normalerweise muss dein Kernel Multiboot-kompatibel sein, wenn du qemu -kernel kernel verwendest. (Außer du hast den irgendwie mit dem Linux-Bootprotokoll kompatibel gemacht. Aber das kenne ich nicht.)

Um herauszufinden, ob der GDT-Eintrag überschrieben wird, musst du dir den Code anschauen und nach Fehlern suchen. Du kannst den Code auch als .zip oder in einem GIT-Repository irgendwo hochladen. Dann kann ich da mal einen Blick drauf werfen.

Edit: Wenn ich raten soll, hast du den Wert für GDT_ENTRIES nicht angepasst.
Zitat
Außerdem müssen wir das TSS noch aktivieren. Dazu fügen wir der GDT einen zusätzlichen Eintrag hinzu und laden anschließend auch das Taskregister TR neu (nicht vergessen, das Limit der GDT zu erhöhen, wenn nötig):
« Letzte Änderung: 01. November 2012, 17:52 von Jidder »
Dieser Text wird unter jedem Beitrag angezeigt.

Roadrunner

  • Beiträge: 33
    • Profil anzeigen
Gespeichert
« Antwort #10 am: 01. November 2012, 18:27 »
Ich hab festgestellt, dass der TSS-Fault nicht im Kernel-Segment auftritt, also nicht während dem Taskwechsel, sondern während der Ausführung eines Tasks. Ist das immer so?
Exception 0x1A: Insufficient user-IQ

Jidder

  • Administrator
  • Beiträge: 1 625
    • Profil anzeigen
Gespeichert
« Antwort #11 am: 01. November 2012, 18:36 »
Wenn du das Tutorial befolgt hast, wird das TSS nur beim Wechsel vom User Space in den Kernel Space genutzt. Ein Taskwechsel mittels TSS (sogenanntes Hardware-Multitasking) wird überhaupt nicht durchgeführt. Der TSS Fault kann während der Task läuft bei dem Aufruf der INT-Instruktion, beim Auftreten eines IRQs oder einer Exception auftreten.
Dieser Text wird unter jedem Beitrag angezeigt.

Roadrunner

  • Beiträge: 33
    • Profil anzeigen
Gespeichert
« Antwort #12 am: 02. November 2012, 12:02 »
Warum kann ich den TSS Fault eigentlich nicht abfangen? Schließlich habe ich folgenden Code im Interrupthandler:

if(sf->interrupt <= 0x1f) //Exceptions
    {
        switch (sf->interrupt)
        {
            case 10: clrscr();
                     kprint(0x0F, "TSS FAULT; EC: %X", sf->ec);
                     asm volatile("hlt");
                     break;

Aber mein Handler wird überhaupt nicht mehr aufgerufen. Es kommt einfach ein Double Fault (den man mir übrigens auch nicht meldet) und dann eben ein Triple Fault. Wie kann das sein?
Exception 0x1A: Insufficient user-IQ

tiger717

  • Beiträge: 84
    • Profil anzeigen
Gespeichert
« Antwort #13 am: 02. November 2012, 12:10 »
Werden denn andere Exceptions aufgerufen? Double Fault + Triple Fault deutet auf fehlerhaften IDT hin. Probier doch einfach mal
asm volatile("xor ebx, ebx; div ebx");
Das sollte eine Divide-through-Zero-Exception auslösen.

EDIT: Arrghh, gcc/gas nutzen ja AT/T-Syntax. Dann also
asm volatile("xor %ebx, %ebx; div %ebx");
« Letzte Änderung: 02. November 2012, 12:12 von tiger717 »

Roadrunner

  • Beiträge: 33
    • Profil anzeigen
Gespeichert
« Antwort #14 am: 02. November 2012, 12:17 »
Ich probiers gleich mal. Aber vorher noch was anderes:

asm volatile(".intel_syntax; ... ; .ATT_syntax;");

Damit kannst du auch die Intel-Syntax verwenden. Du musst aber aufpassen, dass du auch wieder ".ATT_syntax" schreibst, weil sonst der Assembler einige scheinbar unerklärliche Fehlermeldungen ausgibt.

EDIT: So. Divide By Zero funktioniert - ich bekomme also eine Exception.

Und im Kernel kann ich mit
int 0x0aauch den Handler aufrufen.
« Letzte Änderung: 02. November 2012, 12:31 von Roadrunner »
Exception 0x1A: Insufficient user-IQ

Jidder

  • Administrator
  • Beiträge: 1 625
    • Profil anzeigen
Gespeichert
« Antwort #15 am: 02. November 2012, 13:48 »
Der Sinn vom Double Fault ist eine zusätzliche "saubere" Umgebung bereitzustellen, um dann einen vorangegangen Fault wie den TSS-Fault behandeln zu können. Diese Umgebung ist "sauber" in dem Sinne, dass diese vermeidbaren Faults wie dein TSS-Fault ausgeschlossen sind. Weil dein Double Fault aber vermutlich dasselbe defekte TSS nutzt, wird ein Triple Fault ausgelöst, und dein Handler wird nicht aufgerufen.

Da du bisher nicht verraten hast, was genau du nach meinen vorherigen Antworten gemacht hast, und auch den Code nicht hergezeigt hast, kann ich dir nur sagen, dass irgendwas grundlegendes (defekte TSS, GDT, IDT) schief läuft. Das Problem ist der erste TSS Fault. Wenn du den behebst, hast du kein Problem mit dem Double Fault.

EDIT: So. Divide By Zero funktioniert - ich bekomme also eine Exception.

Und im Kernel kann ich mit
int 0x0aauch den Handler aufrufen.
Du solltest genau darauf achten, was das eigentliche Problem ist. Wenn du im Kernel irgendwas ausprobierst, bringt dir das nicht viel, weil das Problem beim Wechsel vom User Mode in den Kernel Mode auftritt (ansonsten wäre das TSS nicht involviert).
Dieser Text wird unter jedem Beitrag angezeigt.

Roadrunner

  • Beiträge: 33
    • Profil anzeigen
Gespeichert
« Antwort #16 am: 02. November 2012, 17:58 »
Also hier der Code für meine GDT:

struct gdt_entry
{
    uint8_t limit_0_7;
    uint8_t limit_8_15;
    uint8_t base_0_7;
    uint8_t base_8_15;
    uint8_t base_16_23;
    uint8_t access;
    uint8_t flags_limit;
    uint8_t base_24_31;
}__attribute__((packed));

typedef struct gdt_entry gdt_entry;

gdt_entry gdt[GDT_ENTRIES];

struct gdt_ptr
{
    uint16_t gdt_limit;
    void *gdt_ptr;
}__attribute__((packed)) gdt_p = {.gdt_limit = GDT_ENTRIES*8-1, .gdt_ptr = gdt};


void gdt_set_entry(uint32_t limit, uint32_t base, uint8_t accessbyte, uint8_t flags, unsigned int index)
{
    gdt_entry *g = &(gdt[index]);

    g->limit_0_7 = (uint8_t) limit;
    g->limit_8_15 = (uint8_t) (limit >> 8);
    g->base_0_7 = (uint8_t) base;
    g->base_8_15 = (uint8_t) (base >> 8);
    g->base_16_23 = (uint8_t) (base >> 16);
    g->access = accessbyte;
    g->flags_limit = (((uint8_t) (limit >> 16)) & 0b00001111) | (flags << 4);
    g->base_24_31 = (uint8_t) (base >> 24);
}

void setup_gdt()
{
    gdt_set_entry(0, 0, 0, 0, 0);
    gdt_set_entry(0xFFFFFFFF, 0x00000000, 0x9A, 0xD, 1); //Kernel-Code
    gdt_set_entry(0xFFFFFFFF, 0x00000000, 0x92, 0xD, 2); //Kernel-Data
    gdt_set_entry(0xFFFFFFFF, 0x00000000, 0xFA, 0xD, 3); //User-Code
    gdt_set_entry(0xFFFFFFFF, 0x00000000, 0xF2, 0xD, 4); //User-Data
    gdt_set_entry(((uint32_t) tss_ptr) + sizeof(tss_entry), (uint32_t) tss_ptr, 0xE9, 0, 5);
    asm volatile("lgdt %0" : : "m" (gdt_p));
    asm volatile("ltr %%ax" : : "a" (5 << 3));

    gdt_reload();

    tss_ptr->ss0 = gdt_get_selector(2, 0, 0b00);
    tss_ptr->esp0 = (uint32_t) kernel_stack;
}

Hier ist GDT_ENTRIES auf 7 festgelegt. tss_ptr ist ein Zeiger auf ein Objekt der folgenden Struktur:
struct tss_entry_struct
{
   uint32_t prev_tss;
   uint32_t esp0;
   uint32_t ss0;
   uint32_t esp1;
   uint32_t ss1;
   uint32_t esp2;
   uint32_t ss2;
   uint32_t cr3;
   uint32_t eip;
   uint32_t eflags;
   uint32_t eax;
   uint32_t ecx;
   uint32_t edx;
   uint32_t ebx;
   uint32_t esp;
   uint32_t ebp;
   uint32_t esi;
   uint32_t edi;
   uint32_t es;
   uint32_t cs;
   uint32_t ss;
   uint32_t ds;
   uint32_t fs;
   uint32_t gs;
   uint32_t ldt;
   uint16_t trap;
   uint16_t iomap_base;
} __packed;

Und schließlich die IDT:

idt:
    times IDT_ENTRIES*8 db 0

idtr:
    dw (IDT_ENTRIES*8)-1
    dd idt

setup_idt:
    lidt [idtr]

%macro idt_entry 1
    mov edx, interrupt_stub_%1
    mov [idt+8*(%1)], dx
    call get_code_segment
    mov word [idt+8*(%1)+2], ax
    mov word [idt+8*(%1)+4], 0x8E00
    shr edx, 16
    mov [idt+8*(%1)+6], dx
%endmacro

%macro idt_entry_ec 1
    mov edx, interrupt_stub_ec_%1
    mov [idt+8*(%1)], dx
    call get_code_segment
    mov word [idt+8*(%1)+2], ax
    mov word [idt+8*(%1)+4], 0x8E00
    shr edx, 16
    mov [idt+8*(%1)+6], dx
%endmacro

%macro idt_entry_user 1
    mov edx, interrupt_stub_%1
    mov [idt+8*(%1)], dx
    call get_code_segment
    mov word [idt+8*(%1)+2], ax
    mov word [idt+8*(%1)+4], 0xEE00
    shr edx, 16
    mov [idt+8*(%1)+6], dx
%endmacro

    idt_entry 0
    idt_entry 1
    ...
In den entsprechenden Handlern wird dann der Standart-Handler aufgerufen. Der entspricht wieder dem im Tutorial.
Exception 0x1A: Insufficient user-IQ

tiger717

  • Beiträge: 84
    • Profil anzeigen
Gespeichert
« Antwort #17 am: 02. November 2012, 19:09 »
TSS wurde initialisiert? memclr()? :roll:

Jidder

  • Administrator
  • Beiträge: 1 625
    • Profil anzeigen
Gespeichert
« Antwort #18 am: 02. November 2012, 20:28 »
    gdt_set_entry(((uint32_t) tss_ptr) + sizeof(tss_entry), (uint32_t) tss_ptr, 0xE9, 0, 5);
Limit gibt die Größe an, nicht das Ende. Also einfach sizeof(tss_entry). (-1 um genau zu sein)

Wie wird das Objekt, auf das tss_ptr zeigt, angelegt?
Dieser Text wird unter jedem Beitrag angezeigt.

Roadrunner

  • Beiträge: 33
    • Profil anzeigen
Gespeichert
« Antwort #19 am: 02. November 2012, 20:50 »
Das Objekt wird mit tss_entry tss; angelegt. Anschließend wird alles auf 0 gesetzt und für ss0 der Wert 0x10, also der Selektor für das Kernel-Datensegment, eingetragen.
Exception 0x1A: Insufficient user-IQ

 

Einloggen