IRQ和iret指令在32位内核上的工作语义(保护模式)

问题描述 投票:3回答:1

我一直在编写一个业余操作系统,我正在尝试在内核中进行中断/异常处理。我在环0中,所以没有特权堆栈切换等。这些是我的例程:

#include <stdint.h>
#include "dadio.h"

#define MAX_INTERRUPTS 256
#define IDT_DESC_BIT16 0x06 //00000110
#define IDT_DESC_BIT32 0x0E //00001110
#define IDT_DESC_RING1 0x40 //01000000
#define IDT_DESC_RING2 0x20 //00100000
#define IDT_DESC_RING3 0x60 //01100000
#define IDT_DESC_PRESENT 0x80//10000000

//Structs used in this routine

typedef struct __attribute__ ((__packed__)) idtr {
    uint16_t        limit;
    uint32_t        base;
}idtr_t;

typedef struct __attribute__ ((__packed__)) gdtr {
    uint16_t        limit;
    uint32_t        base;
}gdtr_t;

typedef struct __attribute__ ((__packed__)) idt_descriptor {
uint16_t        baseLo;
uint16_t        sel;
uint8_t         reserved;
uint8_t         flags;
uint16_t        baseHi;
}idt_descriptor_t;

typedef struct __attribute__((__packed__)) gdt_descriptor {
uint16_t        limit;
uint16_t        baseLo;
uint8_t         baseMid;
uint16_t        flags;
uint8_t         baseHi;
} gdt_descriptor_t;

//External assembly functions
void init_pic();
void install_idt(idtr_t* address);
void enable_interrupts();

//Global variables in this routine
static idt_descriptor_t _idt[MAX_INTERRUPTS];
static idtr_t _idtr; //This will be the 6 byte base + limit

//Helper functions
static void install_ir(uint32_t index,uint16_t flags, uint16_t sel, uint32_t* handler_address);
static void default_handler();



void idt_init()
{
    _idtr.base = (uint32_t)_idt;
    _idtr.limit = (sizeof (idt_descriptor_t) * MAX_INTERRUPTS) -1 ;

    for (int i=0;i<MAX_INTERRUPTS;i++)
    {
        _idt[i].baseLo = 0;
        _idt[i].sel = 0;
        _idt[i].reserved = 0;
        _idt[i].flags = 0;
        _idt[i].baseHi = 0;
    }

    for (int i=0;i<MAX_INTERRUPTS;i++)
        install_ir(i,IDT_DESC_BIT32 | IDT_DESC_PRESENT, 0x08, (uint32_t*) default_handler);

    init_pic();
    install_idt(& _idtr);
    enable_interrupts();
}
static void install_ir(uint32_t index,uint16_t flags, uint16_t sel, uint32_t* handler_address)
{
    if (index >=MAX_INTERRUPTS) return;

    _idt[index].baseLo = (uint32_t)handler_address & 0xffff;
    _idt[index].baseHi = ((uint32_t)handler_address >> 16) & 0xffff;
    _idt[index].reserved = 0;
    _idt[index].flags = flags;
    _idt[index].sel = sel;
}

static void default_handler()
{
    monitor-puts("This is the default exception handler"); //This is a routine that prints messages on the screen... The gist is that it writes to 0xb8000 and so on...
    for (;;);
}

组装例程

init_pic:
    mov al, 0x11  ;ICW 1  ;Expect IC4|single?|0|level?|init?|000
    out 0x20,al
    out 0xA0,al

    mov al,0x20  ;Remapping the IRQs
    out 0x21,al
    mov al,0x28
    out 0xA1,al

    ; Send ICW 3 to primary PIC
    mov al, 0x4  ; 0x4 = 0100 Second bit (IR Line 2)
    out 0x21, al ; write to data register of primary PIC

    ; Send ICW 3 to secondary PIC
    mov al, 0x2 ; 010=> IR line 2
    out 0xA1, al ; write to data register of secondary PIC

    ; Send ICW 4 - Set x86 mode --------------------------------

    mov al, 1   ; bit 0 enables 80x86 mode

    out 0x21, al
    out 0xA1, al

    ; Zeroing out the data registers

    mov al, 0
    out 0x21, al
    out 0xA1, al

    ret

enable_interrupts:
    sti
    ret

最小内核是:

void kmain()
{
idt_init();
return;
}

如果我注释掉idt_init函数中的init_pic()],>则得到消息:这是默认的异常处理程序,后跟for(;;)。我认为这是可以预料的,因为一旦启用中断,类似计时器的东西就会发送一个IRQ,并且由于默认情况下它映射到(除以零?)异常,所以我得到了已定义的处理程序消息。

但是如果我取消注释init_pic()

,则不会收到该消息。我知道IRQ(0-15)已重新映射为中断向量(32-47)。但是计时器中断仍然会触发,我应该得到消息。 (我认为,所有256种可能的中断/异常都映射到同一例程中)。我在哪里弄错了?

还有一个小的后续问题。我知道有些异常会推送错误代码,而有些则不会。但是iret指令不知道对吗?那么程序员是否有责任手动添加到esp中(弹出异常的错误代码确实会推送错误),然后执行iret?

我从理解的地方阅读了80386开发人员手册。我在某个地方错了吗?

PS:尽管我的项目中有很多代码,我试图提供最少的代码。

我一直在编写一个业余操作系统,我正在尝试在内核中进行中断/异常处理。我在环0中,所以没有特权堆栈切换等。这些是我的例程:#include <...>

c assembly x86 interrupt osdev
1个回答
0
投票

关键点可能会在kmain返回时发生什么?

© www.soinside.com 2019 - 2024. All rights reserved.