idt aufsetzen



  • Moin!
    Nachdem ich jetzt den Sprung in den PM geschafft habe (siehe http://www.c-plusplus.net/forum/viewtopic-var-t-is-122419.html) will ich bei den Interrupts weitermachen. Inzwischen habe ich ein paar rudimentäre Funktionen in C geschrieben (Consolen-I/O). Nun versuche ich die idt zu laden, aber das hat sich als schwerer entpuppt als ich dachte.
    Ich möchte vorerst alle 256 interrupts mit einer ISR belegen, die nichts anderes Tut, als einen Buchstaben in der linken oberen Ecke anzuzeigen. Mein Code sieht folgendermaßen aus:

    #include <pmode.h>
    
    void _setup_idt();
    int main()
    {
        ...
        _setup_idt();
        ...(endlossschleife)...
    }
    
    void fill_idt_entry(_idt_entry* entr, _u32 func, _u16 sel, _u8 access, _u8 param_cnt)
    {
    	entr->selector = sel;
    	entr->offs1 = func & 0xFFFF;
    	entr->offs2 = func >> 16;
    	entr->flags = access;
    	entr->reserved = param_cnt;
    }
    
    #define NUM_IDT_ENTRIES	256
    
    extern void interrupt0();
    void _setup_idt()
    {
    	static _idt_pointer idt __attribute__((aligned(2)));
    	static _idt_entry entries[NUM_IDT_ENTRIES] __attribute__((aligned(2)));
    
    	idt.base = (_u32)entries;
    	idt.limit = 8*NUM_IDT_ENTRIES;
    
    	for(int i = 0; i < NUM_IDT_ENTRIES; i++)
    		fill_idt_entry(&entries[i], (_u32)interrupt0, 8, ACS_INT, 0);
    
    	asm volatile("lidt (%%eax)\n" ::"a"(&idt));
    	asm volatile("sti"::);
    }
    

    Die Headerdatei pmode.h:

    #ifndef __UUID_KERNEL_PMODE_H
    #define __UUID_KERNEL_PMODE_H
    
    #include <types.h>
    
    #define IDTENTRY_DPL_0 0x0
    #define IDTENTRY_DPL_1 0x20
    #define IDTENTRY_DPL_2 0x40
    #define IDTENTRY_DPL_3 0x60
    
    #define IDTENTRY_PRESENT 0x80
    
    typedef struct {
    	_u16	offs1;
    	_u16	selector;
    	_u8	reserved;
    	_u8	flags;
    	_u16	offs2;
    } _idt_entry;
    
    typedef struct {
    	_u16 limit;
    	_u32 base;
    } _idt_pointer;
    
    /* Access byte's flags */
    #define ACS_PRESENT     0x80            /* present segment */
    #define ACS_CSEG        0x18            /* code segment */
    #define ACS_DSEG        0x10            /* data segment */
    #define ACS_CONFORM     0x04            /* conforming segment */
    #define ACS_READ        0x02            /* readable segment */
    #define ACS_WRITE       0x02            /* writable segment */
    #define ACS_IDT         ACS_DSEG        /* segment type is the same type */
    #define ACS_INT_GATE    0x06            /* int gate for 286 */
    #define ACS_INT         (ACS_PRESENT | ACS_INT_GATE) /* present int gate */
    
    /* Ready-made values */
    #define ACS_CODE        (ACS_PRESENT | ACS_CSEG | ACS_READ)
    #define ACS_DATA        (ACS_PRESENT | ACS_DSEG | ACS_WRITE)
    #define ACS_STACK       (ACS_PRESENT | ACS_DSEG | ACS_WRITE)
    
    #endif //__UUID_KERNEL_PMODE_H
    

    types.h:

    #ifndef __UUID_KERNEL_TYPES_H
    #define __UUID_KERNEL_TYPES_H
    
    #define _u8 unsigned char
    #define _s8 char
    #define _u16 unsigned short
    #define _s16 short
    #define _u32 unsigned long
    #define _s32 long
    
    #endif //__UUID_KERNEL_TYPES_H
    

    Und zuguterletzt noch die ISR:

    .section .data
    
    .section .text
    .globl interrupt0
    
    .align 2
    interrupt0:
    	pusha
    	push %gs
    	push %fs
    	push %es
    	push %ds
    
    	movb $0x43, %al
    	movb %al, 0xB8000
    
    	pop %ds
    	pop %es
    	pop %fs
    	pop %gs
    	popa
    	iret
    

    Wenn ich das ganze auf einer echten Maschine ausführe, dann startet die sich einfach neu. Im Simulator bleibt er bei asm volatile("sti"::); hängen. Wenn ich das sti rausnehme, dann reagiert der Simulator (QEmu) zwar, aber die ISRs funktionieren nicht.

    TIA Hackbert



  • hi,

    es könnte sein, dass dein compiler die structs _idt_entry & _idt_pointer falsch interpretier,d.h. dass er ein künstliches 32-bit align zu jedem struct-member hinzufügt. Du könntest einfach mal die Größe der structs ausgeben lassen. Falls die nicht 8 bzw. 5 byte is, müsstes du (bei gcc) folgendes einfügen:

    typedef struct {
        _u16    offs1;
        _u16    selector;
        _u8    reserved;
        _u8    flags;
        _u16    offs2;
    } _idt_entry __attribute__((packed));
    
    typedef struct {
        _u16 limit;
        _u32 base;
    } _idt_pointer __attribute__((packed));
    

    Ansonsten seh ich so beim drüberfliegen keinen Fehler in deinem Code 👍



  • Das __attribute__((packed)) hat leider nichts gebracht.
    Der Selektor den ich verwende ich genau der des Codesegments (mit Offset 0 und 4 GiB Größe). Muss man noch ein spezielles Interrupt-Segment in der GDT anlegen???



  • Muss man eventuell explizit den PIC anschalten? Wenn ja: Wie geht das bzw. wo finde ich Infos darüber?



  • hi,

    Wieso verwendest du hier

    for(int i = 0; i < NUM_IDT_ENTRIES; i++)
            fill_idt_entry(&entries[i], (_u32)interrupt0, 1, ACS_INT, 0);
    

    1 als Selektor??? Der Selektor bezieht sich auf die GDT. Falls beispielsweise dein Codesegmentdeskriptor den Index (!) 1 in der GDT hat, dann musst du als Selektor 0x08 verwenden. Aufbau eines Selektors findest du im Intel Architecture Software Developer's Manual Volume 3 - System Programming Guide.
    Wieso verwendest du im selben Codeabschnitt ein 286-Interrupt-Gate? Du bist wahrscheinlich (wie ich dem anderen Thread entnehme) im 32Bit Protected-Mode, da muss man dann schon ein 386-Interrupt-Gate nehmen (0x0E)!
    Nur zur Info: Alles mit 286 is 16Bit. Alles mit 386+ is 32Bit.



  • hackbert schrieb:

    Muss man eventuell explizit den PIC anschalten? Wenn ja: Wie geht das bzw. wo finde ich Infos darüber?

    Ja man sollte die PIC initialisieren, da sie im real-mode die IRQ (=Interrupt Requests) 0-0x0F auf die Interrupts 0x08-0x17 abgebildet. Das Problem daran ist, dass im Protected-Mode alle Interrupts von 0x00 bis 0x20 (ausschließlich) für den Prozessor reserviert sind, d.h. du musst das IRQ to Interrupt maping der PIC verändern. Auf Bona Fide (http://www.osdever.net/ gibts beispielsweise ein Tutorial (http://www.osdever.net/tutorials/pdf/pic.pdf). Vielleicht auch dieses über irq's.



  • Der Tipp mit dem PIC war super! Jetzt startet der Rechner nicht mehr neu 👍

    Nur wird meine ISR net aufgerufen...



  • Muss man neben sti und dem Aktivieren des PICs noch andere Aktionen durchführen um Interrupts zu benutzen?



  • Hi.

    Bei deinem ganzen Code habe ich irgendwo den Ueberblick verloren. Um wieder etwas mehr Klarheit in die Sache zu bringen, moechte ich dich bitten, klar zwischen "Software Interrupts" (aufgerufen mit "int") und "Hardware Interrupts" (IRQs, Exceptions) zu unterscheiden.
    Um interrupts ueber int aufzurufen, brauchst du naemlich weder am PIC, noch am Interrupt-Flag zu drehen.

    Wenn du also nichts mit den IRQs machen willst, solltest du den PIC einfach ausgeschaltet und das Interrupt-Flag geloescht lassen. Wenn die IDT stimmt und deine ISR richtig funktioniert, muesste dann alles laufen.

    Fuer IRQs nochmal der Hinweis: Das Freischalten des PIC allein reicht nicht - du musst den Basisinterrupt fuer den primaeren PIC auf jeden Fall auf eine Nummer > 20h legen. Dann noch ein sti, und zumindest der PIT-IRQ (0) muesste regelmaessig ausgeloest werden.



  • Also. Ich habe erstmal alle Interruppts mit einer ISR versorgt. Das allerdings nur zu Testzwecken! Ich will später sowohl Software-Interrupts, als auch Interrupts von Hardware und Exceptions bearbeiten. Ich brauchte nur erstmal was zum Testen um zu sehen, ob mein IDT richtig ist. PIC1 und 2 arbeiten nun im kaskadierenden Modus, es wurden also die Interrupts richtig für den PM gemappt.

    Hackbert

    <edit>ein manuelles Auslösen eines Software-Interrupts führt derzeit zu Problemen: asm volatile("int $0x80");</edit>

    Die PICs initialisiere ich vor dem sti so:

    #include <pmode.h>
    #include <pic.h>
    
    void _init_pic()
    {
    	_init_pics_cascade(0x20, 0x28);
    }
    
    #define PIC1 0x20
    #define PIC2 0xA0
    
    #define ICW1 0x11
    #define ICW4 0x01
    
    /* _init_pics_cascade()
     * init the PICs and remap them
     */
    void _init_pics_cascade(int pic1, int pic2)
    {
    	/* send ICW1 */
    	outb(PIC1, ICW1);
    	outb(PIC2, ICW1);
    
    	/* send ICW2 */
    	outb(PIC1 + 1, pic1);	/* remap */
    	outb(PIC2 + 1, pic2);	/*  pics */
    
    	/* send ICW3 */
    	outb(PIC1 + 1, 4);	/* IRQ2 -> connection to slave */
    	outb(PIC2 + 1, 2);
    
    	/* send ICW4 */
    	outb(PIC1 + 1, ICW4);
    	outb(PIC2 + 1, ICW4);
    
    	/* disable all IRQs */
    	outb(PIC1 + 1, 0xFF);
    }
    


  • bluecode schrieb:

    hi,

    Wieso verwendest du hier

    for(int i = 0; i < NUM_IDT_ENTRIES; i++)
            fill_idt_entry(&entries[i], (_u32)interrupt0, 1, ACS_INT, 0);
    

    1 als Selektor??? Der Selektor bezieht sich auf die GDT. Falls beispielsweise dein Codesegmentdeskriptor den Index (!) 1 in der GDT hat, dann musst du als Selektor 0x08 verwenden. Aufbau eines Selektors findest du im Intel Architecture Software Developer's Manual Volume 3 - System Programming Guide.
    Wieso verwendest du im selben Codeabschnitt ein 286-Interrupt-Gate? Du bist wahrscheinlich (wie ich dem anderen Thread entnehme) im 32Bit Protected-Mode, da muss man dann schon ein 386-Interrupt-Gate nehmen (0x0E)!
    Nur zur Info: Alles mit 286 is 16Bit. Alles mit 386+ is 32Bit.

    Wollts nur noch mal sagen, weil ich nicht glaub, dass du 's gelesen hast... 😃



  • *lol* Respekt, dass du das alles durchgehst - vielleicht hilft's ja, wenn ich dich quote...? 😃



  • bluecode schrieb:

    Wollts nur noch mal sagen, weil ich nicht glaub, dass du 's gelesen hast... 😃

    Doch, doch. Gelesen habe ich das. Und geändert habe ich es auch. gleicher Effekt: auch mit Selektor 8 ist nichts anzufangen...

    <edit>Auch das definieren von ACS_INT_GATE auf:

    #define ACS_INT_GATE    0x0E
    

    ändert nichts...</edit>


Anmelden zum Antworten