Page 1 of 1

IRQ Double Faulting <SOLVED>

Posted: Tue Aug 09, 2016 1:13 pm
by amanuel2x
Hello Everyone. I have a problem, with the making of my first driver(PIT). The problem is clearly explained in this viedo i recorded now: https://www.youtube.com/watch?v=hjvTtVb ... e=youtu.be . Let me show my timer driver first:

timer.c++:

Code: Select all

#include "timer.h"

/* This will keep track of how many ticks that the system
*  has been running for */

typedef void(*regs_func)(struct regs *r);


static int32_t timer_ticks = 0;

extern void install_handler_irq(int irq, regs_func handler);


/* Handles the timer. In this case, it's very simple: We
*  increment the 'Timer::timer_ticks' variable every time the
*  timer fires. By default, the timer fires 18.222 times
*  per second. Why 18.222Hz? Some engineer at IBM must've
*  been smoking something funky */
void timer_handler_driver(struct regs *r)
{
    /* Increment our 'tick count' */
    timer_ticks++;

    /* Every 18 clocks (approximately 1 second), we will
    *  display a message on the screen */
//    if (timer_ticks % 18 == 0)
//    {
        printf("One second has passed\n");
//    }
}

Timer::Timer()
{
}

/* This will continuously loop until the given time has
*  been reached */
void Timer::timer_wait(int ticks)
{
    unsigned long eticks;

    eticks = timer_ticks + ticks;
    while((unsigned)timer_ticks < eticks);
}

void Timer::install_timer()
{
    install_handler_irq(0, timer_handler_driver);
}

/* Sets up the system clock by installing the timer handler
*  into IRQ0 */
Timer::~Timer()
{

}

and here is my irq c++ code:

irq.c++:

Code: Select all

#include "irq.h"

#define PIC_MASTER_CONTROL 0x20
#define PIC_MASTER_MASK 0x21
#define PIC_SLAVE_CONTROL 0xa0
#define PIC_SLAVE_MASK 0xa1


typedef void(*regs_func)(struct regs *r);


/*Get all irq's*/
extern "C" void irq0(void);
extern "C" void irq1(void);
extern "C" void irq2(void);
extern "C" void irq3(void);
extern "C" void irq4(void);
extern "C" void irq5(void);
extern "C" void irq6(void);
extern "C" void irq7(void);
extern "C" void irq8(void);
extern "C" void irq9(void);
extern "C" void irq10(void);
extern "C" void irq11(void);
extern "C" void irq12(void);
extern "C" void irq13(void);
extern "C" void irq14(void);
extern "C" void irq15(void);


extern void panic(const char* exception);

regs_func irq_routines[16] = {
	 0,0,0,0,0,0,0,0
	,0,0,0,0,0,0,0,0
};

static PORT::Port8Bits p8b_irq;
static SerialPort sp_irq;

//Basically a declaration of IDT_ENTRY in 
//idt.c++
struct idt_entry {
    uint16_t base_lo;
    uint16_t sel; // Kernel segment goes here.
    uint8_t always0;
    uint8_t flags; // Set using the table.
    uint16_t base_hi;
}__attribute__((packed));

//Get the Exact IDT array from idt.c++
extern struct idt_entry idt[256];

static inline void idt_set_gate(uint8_t num, void(*handler)(void), uint16_t sel,
        	  uint8_t flags) 
{
    idt[num].base_lo = (uintptr_t)handler >> 0 & 0xFFFF;
    idt[num].base_hi = (uintptr_t)handler >> 16 & 0xffff;
    idt[num].always0 = 0;
    idt[num].sel = sel;
    idt[num].flags = flags;
}

IRQ::IRQ(){}; 
IRQ::~IRQ(){};

/* Normally, IRQs 0 to 7 are mapped to entries 8 to 15. This
*  is a problem in protected mode, because IDT entry 8 is a
*  Double Fault! Without remapping, every time IRQ0 fires,
*  you get a Double Fault Exception, which is NOT actually
*  what's happening. We send commands to the Programmable
*  Interrupt Controller (PICs - also called the 8259's) in
*  order to make IRQ0 to 15 be remapped to IDT entries 32 to
*  47 */
void IRQ::irq_remap()
{

        // ICW1 - begin initialization
    p8b_irq.out(0x11,PIC_MASTER_CONTROL);
    p8b_irq.out(0x11,PIC_SLAVE_CONTROL);

    // Remap interrupts beyond 0x20 because the first 32 are cpu exceptions
    p8b_irq.out(0x21,PIC_MASTER_MASK);
    p8b_irq.out(0x28,PIC_SLAVE_MASK);

    // ICW3 - setup cascading
    p8b_irq.out(0x04,PIC_MASTER_MASK);
    p8b_irq.out(0x02,PIC_SLAVE_MASK);

    // ICW4 - environment info
    p8b_irq.out(0x01,PIC_MASTER_MASK);
    p8b_irq.out(0x01,PIC_SLAVE_MASK);

    // mask interrupts
    p8b_irq.out(0,PIC_MASTER_MASK);
    p8b_irq.out(0,PIC_SLAVE_MASK);
}

void install_handler_irq(int irq, regs_func handler)
{
    printf(" \n Installer IRQ %d \n ", irq);
	irq_routines[irq] = handler;
}

void uninstall_handler_irq(int irq)
{
	irq_routines[irq] = 0;
} 




/* First remap the interrupt controllers, and then we install
*  the appropriate ISRs to the correct entries in the IDT. This
*  is just like installing the exception handlers */

void IRQ::install_irqs()
{
	this->irq_remap();
    idt_set_gate(32, irq0, 0x08, 0x8E);
    idt_set_gate(33, irq1, 0x08, 0x8E);
    idt_set_gate(34, irq2, 0x08, 0x8E);
    idt_set_gate(35, irq3, 0x08, 0x8E);
    idt_set_gate(36, irq4, 0x08, 0x8E);
    idt_set_gate(37, irq5, 0x08, 0x8E);
    idt_set_gate(38, irq6, 0x08, 0x8E);
    idt_set_gate(39, irq7, 0x08, 0x8E);
    idt_set_gate(40, irq8, 0x08, 0x8E);
    idt_set_gate(41, irq9, 0x08, 0x8E);
    idt_set_gate(42, irq10, 0x08, 0x8E);
    idt_set_gate(43, irq11, 0x08, 0x8E);
    idt_set_gate(44, irq12, 0x08, 0x8E);
    idt_set_gate(45, irq13, 0x08, 0x8E);
    idt_set_gate(46, irq14, 0x08, 0x8E);    
    idt_set_gate(47, irq15, 0x08, 0x8E);
}

/* Each of the IRQ ISRs point to this function, rather than
*  the 'fault_handler' in 'isrs.c'. The IRQ Controllers need
*  to be told when you are done servicing them, so you need
*  to send them an "End of Interrupt" command (0x20). There
*  are two 8259 chips: The first exists at 0x20, the second
*  exists at 0xA0. If the second controller (an IRQ from 8 to
*  15) gets an interrupt, you need to acknowledge the
*  interrupt at BOTH controllers, otherwise, you only send
*  an EOI command to the first controller. If you don't send
*  an EOI, you won't raise any more IRQs */
extern "C" void irq_handler(struct regs *r)
{
    printf("IRQ Being Handled");
}
and my irq.S:

Code: Select all

.section .text
.extern irq_handler
.extern test_func

        .macro irq number
            .global irq\number
            irq\number:
                cli
               pushl $0
                pushl $\number
               jmp common_handler_irq
        .endm

common_handler_irq:
      # save registers
            
            
            pusha

       # call C++ Handler
           call irq_handler

       # restore registers
            popa
            iret

#TODO FOR LOOP
irq 0
irq 1
irq 2
irq 3
irq 4
irq 5
irq 6
irq 7
irq 8
irq 9
irq 10
irq 11
irq 12
irq 13
irq 14
irq 15
Before I start my github is here, so you can see my whole code: https://github.com/amanuel2/OS_Mirror .So what happenes basically is. As you can see on my timer.c++ if i have the if statements commented it prints One Second Passed and the errors on qemu(double fault), if i dont it dosent print anything and double faults. as you can see by the viedo: https://www.youtube.com/watch?v=hjvTtVb ... e=youtu.be. Help Would Be Appreciated!

EDIT:

Sometimes it decides to just show the error message on qemu , and dosent say One Second Passed at all, or even the Divide by 0 error, and gives me this :

Code: Select all

qemu: fatal: Trying to execute code outside RAM or ROM at 0x491019d0

EAX=00000000 EBX=00009500 ECX=00000700 EDX=0010159e
ESI=00000000 EDI=00109000 EBP=00107122 ESP=001031ee
EIP=001019d0 EFL=00000002 [-------] CPL=0 II=0 A20=1 SMM=0 HLT=0
ES =0010 00000000 ffffffff 00cf9300 DPL=0 DS   [-WA]
CS =0008 49000000 0008ffff 00589a00 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 0000ffff 00008200 DPL=0 LDT
TR =0000 00000000 0000ffff 00008b00 DPL=0 TSS32-busy
GDT=     00103114 00000017
IDT=     00102900 000007ff
CR0=00000011 CR2=00000000 CR3=00000000 CR4=00000000
DR0=00000000 DR1=00000000 DR2=00000000 DR3=00000000 
DR6=ffff0ff0 DR7=00000400
CCS=00000010 CCD=001031ce CCO=ADDL    
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
make: *** [Makefile:56: qemu] Aborted (core dumped)
The problem is that its changing.... Its not a stable error as you can see on viedo: https://www.youtube.com/watch?v=fHQE_vI ... e=youtu.be

Re: IRQ Double Faulting

Posted: Tue Aug 09, 2016 6:35 pm
by BrightLight

Code: Select all

    // Remap interrupts beyond 0x20 because the first 32 are cpu exceptions
    p8b_irq.out(0x21,PIC_MASTER_MASK);
    p8b_irq.out(0x28,PIC_SLAVE_MASK);
In the first line, you are using INT 0x21 for the master PIC. That means IRQ 7 (INT 0x28) conflicts with IRQ 8 (INT 0x28 also). Your PIC initialization is wrong.

Re: IRQ Double Faulting

Posted: Wed Aug 10, 2016 12:45 am
by Octocontrabass
When an IRQ occurs, the CPU pushes the return information (used by IRET) onto the stack. You push some more data onto the stack, then call irq_handler.

Code: Select all

               pushl $0
                pushl $\number

            pusha

           call irq_handler
Does irq_handler take any parameters? Are you passing those parameters?

After irq_handler returns, you pop some data from the stack and then return from the interrupt.

Code: Select all

            popa
            iret
Are you popping the same amount of data you pushed? Will IRET pop the return data pushed by the CPU, or something else?
omarrx024 wrote:In the first line, you are using INT 0x21 for the master PIC. That means IRQ 7 (INT 0x28) conflicts with IRQ 8 (INT 0x28 also). Your PIC initialization is wrong.
The PIC ignores the low three bits of ICW2. Despite what the code says, the master PIC is being programmed to start at interrupt 0x20, not interrupt 0x21.

Re: IRQ Double Faulting

Posted: Wed Aug 10, 2016 8:54 am
by amanuel2x
Thanks everyone! The problem is now solved. For people wondering what the solution was look at this learning repo:

https://github.com/amanuel2/OS_MIRROR