我正在开发一个简单的内核,尝试实现键盘中断处理程序以摆脱端口轮询。 我一直在使用 QEMU 的
我的整个内核的完整副本可以在GitHub存储库中找到。 一些相关文件:
-kernel
模式(为了减少编译时间,因为使用 grub-mkrescue
生成 iso 需要很长时间),它运行良好,但当我想切换到 -cdrom
模式时,突然开始崩溃。我不知道原因。
最终我意识到,当它从 iso 引导时,它会先运行 GRUB 引导加载程序,然后再引导内核本身。我发现 GRUB 可能会将处理器切换到保护模式,这就引起了问题。
问题: 通常情况下,我只需初始化中断处理程序,按下键后就会被处理。 然而,当我使用 iso 运行我的内核并按下键时,虚拟机会崩溃。 这在 qemu 和 VMWare 中都发生过,因此我认为我的中断肯定有问题。
请注意,只要我不使用 GRUB,代码就能正常工作。 interrupts_init()
(参见下文)是内核函数 main()
中调用的第一件事情之一。
我的整个内核的完整副本可以在GitHub存储库中找到。 一些相关文件:
lowlevel.asm
:
section .text
global keyboard_handler_int
global load_idt
extern keyboard_handler
keyboard_handler_int:
pushad
cld
call keyboard_handler
popad
iretd
load_idt:
mov edx, [esp + 4]
lidt [edx]
sti
ret
interrupts.c
:
#include <assembly.h> // defines inb() and outb()
#define IDT_SIZE 256
#define PIC_1_CTRL 0x20
#define PIC_2_CTRL 0xA0
#define PIC_1_DATA 0x21
#define PIC_2_DATA 0xA1
extern void keyboard_handler_int(void);
extern void load_idt(void*);
struct idt_entry
{
unsigned short int offset_lowerbits;
unsigned short int selector;
unsigned char zero;
unsigned char flags;
unsigned short int offset_higherbits;
} __attribute__((packed));
struct idt_pointer
{
unsigned short limit;
unsigned int base;
} __attribute__((packed));
struct idt_entry idt_table[IDT_SIZE];
struct idt_pointer idt_ptr;
void load_idt_entry(int isr_number, unsigned long base, short int selector, unsigned char flags)
{
idt_table[isr_number].offset_lowerbits = base & 0xFFFF;
idt_table[isr_number].offset_higherbits = (base >> 16) & 0xFFFF;
idt_table[isr_number].selector = selector;
idt_table[isr_number].flags = flags;
idt_table[isr_number].zero = 0;
}
static void initialize_idt_pointer()
{
idt_ptr.limit = (sizeof(struct idt_entry) * IDT_SIZE) - 1;
idt_ptr.base = (unsigned int)&idt_table;
}
static void initialize_pic()
{
/* ICW1 - begin initialization */
outb(PIC_1_CTRL, 0x11);
outb(PIC_2_CTRL, 0x11);
/* ICW2 - remap offset address of idt_table */
/*
* In x86 protected mode, we have to remap the PICs beyond 0x20 because
* Intel have designated the first 32 interrupts as "reserved" for cpu exceptions
*/
outb(PIC_1_DATA, 0x20);
outb(PIC_2_DATA, 0x28);
/* ICW3 - setup cascading */
outb(PIC_1_DATA, 0x00);
outb(PIC_2_DATA, 0x00);
/* ICW4 - environment info */
outb(PIC_1_DATA, 0x01);
outb(PIC_2_DATA, 0x01);
/* Initialization finished */
/* mask interrupts */
outb(0x21 , 0xFF);
outb(0xA1 , 0xFF);
}
void idt_init(void)
{
initialize_pic();
initialize_idt_pointer();
load_idt(&idt_ptr);
}
void interrupts_init(void)
{
idt_init();
load_idt_entry(0x21, (unsigned long) keyboard_handler_int, 0x08, 0x8E);
/* 0xFD is 11111101 - enables only IRQ1 (keyboard)*/
outb(0x21 , 0xFD);
}
kernel.c
#if defined(__linux__)
#error "You are not using a cross-compiler, you will most certainly run into trouble!"
#endif
#if !defined(__i386__)
#error "This kernel needs to be compiled with a ix86-elf compiler!"
#endif
#include <kernel.h>
// These _init() functions are not in their respective headers because
// they're supposed to be never called from anywhere else than from here
void term_init(void);
void mem_init(void);
void dev_init(void);
void interrupts_init(void);
void shell_init(void);
void kernel_main(void)
{
// Initialize basic components
term_init();
mem_init();
dev_init();
interrupts_init();
// Start the Shell module
shell_init();
// This should be unreachable code
kernel_panic("End of kernel reached!");
}
boot.asm
:
bits 32
section .text
;grub bootloader header
align 4
dd 0x1BADB002 ;magic
dd 0x00 ;flags
dd - (0x1BADB002 + 0x00) ;checksum. m+f+c should be zero
global start
extern kernel_main
start:
mov esp, stack_space ;set stack pointer
call kernel_main
; We shouldn't get to here, but just in case do an infinite loop
endloop:
hlt ;halt the CPU
jmp endloop
section .bss
resb 8192 ;8KB for stack
stack_space:
-kernel
一起使用)对_GDTR/GDT_更宽容。我成功地插入了一个_GDT_,并且似乎解决了这个问题。今天晚些时候,我会在另一个链接中更新我的答案。 - Michael Petchkernel_main
函数中调用了load_gdt
函数。 - Michael Petch