How do I use the PIC and PIT timer to schedule processes on the kernel?

In my "wannabe-kernel" I have successfully enabled software interrupt handling via IDT in protected mode. Now I move on to hardware interrupts so that I can assign time slices to processes. The problem is my kernel is not receiving interrupts for PITs.

inline void outb(uint8_t port,unsigned char value)
    {
       __asm__ __volatile__ ("outb %%ax,%%dx": :"d" (port), "a" (value));
    }

    static inline uint8_t inb(uint16_t port)
    {
        uint8_t ret;
        __asm__ __volatile__ ( "inb %1, %0" : "=a"(ret) : "d"(port) );
        return ret;
    }

    #define PIC1    0x20
    #define PIC2  0xA0   
    #define PIC1_COMMAND    PIC1
    #define PIC1_DATA       (PIC1+1)
    #define PIC2_COMMAND    PIC2
    #define PIC2_DATA       (PIC2+1)

    #define ICW1_ICW4       0x01        
    #define ICW1_SINGLE     0x02        
    #define ICW1_INTERVAL4  0x04        
    #define ICW1_LEVEL      0x08        
    #define ICW1_INIT       0x10        

    #define ICW4_8086       0x01    
    #define ICW4_AUTO       0x02        
    #define ICW4_BUF_SLAVE  0x08
    #define ICW4_BUF_MASTER 0x0C    
    #define ICW4_SFNM       0x10

    void init_pic()
    {
        unsigned char a1, a2;
        unsigned offset_master = 0x20;
        unsigned offset_slave = 0x28;
        a1 = inb(PIC1_DATA);                        
        a2 = inb(PIC2_DATA);

        outb(PIC1_COMMAND, ICW1_INIT+ICW1_ICW4);  
        outb(PIC2_COMMAND, ICW1_INIT+ICW1_ICW4);
        outb(PIC1_DATA, offset_master);                 
        outb(PIC2_DATA, offset_slave);                 
        outb(PIC1_DATA, 4);                      
        outb(PIC2_DATA, 2);                       
        outb(PIC1_DATA, ICW4_8086);
        outb(PIC2_DATA, ICW4_8086);
        outb(PIC1_DATA, a1); 
        outb(PIC2_DATA, a2);
    }

    #define PIT_COMMAND 0x43
    #define PIT_DATA1   0x40
    #define PIT_DATA2   0x41
    #define PIT_DATA3   0x42

    void init_pit()
    {
       uint32_t divisor = 1193180 / 50;

       outb(PIT_COMMAND, 0x36);

       uint8_t l = (uint8_t)(divisor & 0xFF);
       uint8_t h = (uint8_t)( (divisor>>8) & 0xFF );

       outb(PIT_DATA1, l);
       outb(PIT_DATA1, h);  
    }

      

And the timer handler

    unsigned tick = 0;
    void timerHandler()
    {
       tick++;
       char* ascii_code;
       itoa(ascii_code, 'd', tick);
       terminal_writestring("PIT Tick: ", &terminal);
       terminal_writestring(ascii_code, &terminal);
       terminal_writestring("\n", &terminal);
    }

      

There are not many details in the barrels about the reason for isse:

00122970752e[CPU0  ] interrupt(): gate descriptor is not valid sys seg       (vector=0x20)
00122970752e[CPU0  ] interrupt(): gate descriptor is not valid sys seg (vector=0x0d)
00122970752e[CPU0  ] interrupt(): gate descriptor is not valid sys seg (vector=0x08)

      

I registered with the IDT timer handler in the same way (only the record number is different) as a software interrupt handler.
EDIT: IDT setup code as requested:

    struct __InteruptDescriptorTableEntry
    {
       uint16_t offset_low;
       uint16_t selector; 
       uint8_t zero;      
       uint8_t type_attr; 
       uint16_t offset_up; 
    } __attribute__((packed));
    typedef struct __InteruptDescriptorTableEntry IDTEntry;

    struct _ITD_PTR
    {
        uint16_t idtSize;
        uint32_t idtBaseAddr;
    } __attribute__((packed));
    typedef struct _ITD_PTR _IDT_PTR;

    void zeroIDT()
    {   
        unsigned i;
        for(i=0;i<NUM_IDT_ENTRIES-1;++i)
        {
            IDTEntry nullIDTEntry = fillIDTEntry(0,0,0);
            registerInterupt(nullIDTEntry, i);
        }
    }

    void registerInterupt(const IDTEntry entry, const unsigned intNo)
    {
        if(intNo < NUM_IDT_ENTRIES)
            InteruptDescriptorTable[intNo] = entry;
    }

    #define LOW_FUN_ADDR(fun) ( (uint32_t)fun & 0xFFFF )
    #define UP_FUN_ADDR(fun) ( (uint32_t)fun >> 16) & 0xFFFF

    IDTEntry fillIDTEntry(uint32_t intHandler,
                          uint16_t selector,   
                          uint8_t type_attr)

    {   IDTEntry newEntry;
        newEntry.offset_low = LOW_FUN_ADDR(intHandler); 
        newEntry.selector = selector; 
        newEntry.zero = 0;     
        newEntry.type_attr = type_attr;
        newEntry.offset_up = UP_FUN_ADDR(intHandler);
        return newEntry;
    }

    extern void _lidt(_IDT_PTR* idtPtr);
    void timerHandler_TEST();

    void loadIDT()
    {
        zeroIDT();
        _IDT_PTR idtPtr;
        idtPtr.idtSize = sizeof(struct __InteruptDescriptorTableEntry)*256 - 1;
        idtPtr.idtBaseAddr = (uint32_t) &InteruptDescriptorTable;

        IDTEntry printOnScreenInt = fillIDTEntry((uint32_t)interupt_pritnOnScreen, 0x18, 0xee);
        registerInterupt(printOnScreenInt, 50);

        IDTEntry timerIntEntry = fillIDTEntry((uint32_t)timerHandler_TEST, 0x18, 0xee);
        registerInterupt(printOnScreenInt, 13);
        _lidt(&idtPtr);
    }

      

Build source:

.global _lidt
_lidt:
    push %ebp
    mov %esp,%ebp
    mov 8(%esp), %eax
    lidt (%eax)
leave
ret

      

EDIT2: I changed the PIT masks to:

a1 = 0x7F; //master                        
a2 = 0xFF; //slave

      

to get only PIT int, but no IRQ delivered (no triple error).
EDIT3: info gdt

and info IDT

:

IDT[0x20]=32-Bit Interrupt Gate target=0x0018:0x001009c0, DPL=3
IDT[0x32]=32-Bit Interrupt Gate target=0x0018:0x001009c0, DPL=3

GDT:
GDT[0x00]=??? descriptor hi=0x00000000, lo=0x00000000
GDT[0x01]=Code segment, base=0x00000000, limit=0xffffffff, Execute/Read, Non-Conforming, Accessed, 32-bit
GDT[0x02]=Data segment, base=0x00000000, limit=0xffffffff, Read/Write, Accessed
GDT[0x03]=Code segment, base=0x00000000, limit=0xffffffff, Execute/Read, Non-Conforming, Accessed, 32-bit
GDT[0x04]=Data segment, base=0x00000000, limit=0xffffffff, Read/Write, Accessed
GDT[0x05]=32-Bit TSS (Busy) at 0x00101000, length 0x00068

      

+3


source to share





All Articles