Я пишу хобби ОС и пытаюсь обработать прерывания / исключения в ядре. Я нахожусь в кольце 0, поэтому нет стекового переключателя между привилегиями и т. Д. c. Это мои процедуры:
#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;
}
Если я закомментирую init_pi c () в функции idt_init , я получаю сообщение: Это обработчик исключений по умолчанию , за которым следует for (;;). Я думаю, что это ожидаемо, потому что, как только я включаю прерывания, что-то вроде таймера отправит IRQ, и, поскольку он по умолчанию сопоставлен с исключением (делить на ноль?), Я получаю сообщение обработчика, которое я определил.
Но если я раскомментирую init_pi c () , я не получу сообщение. Я понимаю, что IRQ (0-15) были преобразованы в векторы прерываний (32 - 47). Но прерывание по таймеру все равно сработает, и я должен получить сообщение. (Я думаю, что все 256 возможных прерываний / исключений отображаются в одной и той же программе в моем случае). Где я ошибся?
Также небольшой вопрос для продолжения. Я знаю, что некоторые исключения будут pu sh код ошибки, а некоторые нет. Но ирет инструкции не может знать это право? Так стоит ли программистам вручную добавлять в esp (вывод кода ошибки исключения делает pu sh ошибку), а затем делать iret?
Я прочитал руководство для разработчиков 80386, откуда я это понимаю. Я где-то ошибаюсь?
PS: я попытался предоставить минимум кода, хотя в моем проекте гораздо больше кода.