簡體   English   中英

在 32 位內核(保護模式)上運行 IRQ 和 iret 指令語義

[英]Working of the IRQs and the iret instruction semantics on a 32 bit kernel (protected mode)

我一直在寫一個業余操作系統,我正在嘗試在內核中進行中斷/異常處理。 我在 ring 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:我嘗試提供最少的代碼,盡管我的項目有更多的代碼。

關鍵的一點可能是當 kmain 返回時會發生什么?

后續問題很重要:您必須在憤怒之前清理堆棧。 僅出於這個原因,將門指向 C 函數確實很麻煩。 大多數人制作兩個裝配存根:

#define SAVE() pusha; push %ds; push %es; push %fs; push %gs
#define RESTORE() pop %gs; pop %fs; pop %es; pop %ds; popa
#define TRAP0(n) .global vec#n; vec#n: \
       pushl $(n<<8); \
       pushl $0; \
       SAVE(); \
       call generic(); \
       RESTORE(); \
       add $8, %esp; \
       iret

#define TRAP1(n) .global vec#n; vec#n: \
       movw $n, 2(%esp); \
       pushl $0; \
       SAVE();\
       call generic(); \
       RESTORE(); \
       add $8, %esp; \
       iret

#define PTRAP(n) .global vec#n; vec#n: \
       movw $n, 2(%esp);\
       pushl $0; SAVE();\
       mov %cr3, %eax; \
       mov %eax, (12*4)(%esp); \
       call generic(); \
       RESTORE(); \
       add $8, %esp; \
       iret

TRAP0(0); TRAP0(1); ... TRAP0(7);
TRAP1(8); TRAP0(9);
TRAP1(10); ...TRAP1(13);
PTRAP(14); TRAP1(15); ...; TRAP1(31);

/* interrupts: */
TRAP0(32); TRAP0(33); ....  TRAP0(47)

所以你安裝 vec0..vec47 到 idt[0]..[47]; 那么您的通用處理程序看起來好像已經傳遞了一個結構:

struct kstk {
     uint32_t seg[4];
     uint32_t reg[8];
     uint32_t cr3;
     uint16_t errc, trap;
     uint32_t ip, cs, fl, sp, ss;
};

無效處理程序(結構 kstk x); ss,sp 是可選的。 (標准免責聲明,我只是將其輸入到消息中,因此可能需要進行一些調整)。

暫無
暫無

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

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