簡體   English   中英

IRQ處理程序三重故障

[英]IRQ Handler Triple Fault

目前,我的IRQ是三重故障,並給出除以0的錯誤。 是我錄制的視頻,將在實際操作中向您展示。

irq.c ++:

#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(0x00,PIC_MASTER_MASK);
    p8b_irq.out(0x00,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(0xff,PIC_MASTER_MASK);
    p8b_irq.out(0xff,PIC_SLAVE_MASK);
}

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

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");
}

irq.S:

.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

就像在irq.c ++中的視頻中所示,如果我刪除在install_irq函數中調用的irq0(),則三重故障將關閉...但是,如果我刪除了它,我不知道如何正確處理計時器驅動程序...

timer.c ++:

#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()
    {

    }

如果您想查看我的整個代碼。 我在github上更新我的代碼: https : //github.com/amanuel2/OS_Mirror 幫助將不勝感激,我已經在這個問題上停留了一段時間。 謝謝閱讀。

irq0()是中斷服務程序,以iret 您不能對該例程進行C調用。 如果您確實要觸發中斷,請使用int指令。

但是,您實際上並不需要手動觸發計時器IRQ,它應該在配置了計時器/ APIC后觸發,並在sti接收到中斷。

附帶說明一下,“通用IRQ處理程序”方法被認為是不好的做法(浪費時鍾周期和污染緩存),然后僅執行手動切換案例分支,不同的IRQ可能需要不同的處理(例如,從設備的EOI) )。 只需將處理程序直接安裝到IDT中即可。


調查摘要:

經過調查,是由重映射代碼引起的錯誤,將掩碼設置為0xff,從而忽略了PIT。 修復此問題(將0設置為mask)會導致三重錯誤,這表明計時器隨后被觸發,但IDT / IRQ鏈中的其他地方將出現問題。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM