目录
21天 Intel CPU BIOS 学习专栏
第7天:中断处理
中断处理
今天我们将深入探讨中断处理在BIOS中的重要性,以及如何在BIOS初始化过程中正确地设置中断向量表、编写中断处理程序。中断处理是BIOS启动过程中的关键步骤之一,它确保硬件中断能够被正确处理,从而保证系统的稳定性和响应性。通过今天的课程,你将了解中断处理的基本概念、主要步骤以及如何编写和测试中断处理模块。
1. 中断处理概述
中断处理是指在计算机运行过程中,当某个事件发生时(如硬件中断、定时器中断等),CPU暂停当前任务,转而去处理中断事件的过程。在BIOS初始化过程中,我们需要设置中断向量表、编写中断处理程序,以确保中断能够被正确处理。
2. 设置中断向量表
中断向量表(Interrupt Vector Table, IVT)是一个数组,用于存储中断处理程序的地址。每个中断都有一个固定的中断向量号,对应于IVT中的一个条目。
主要步骤
- 初始化中断向量表:创建一个中断向量表,并将其加载到内存中。
- 设置中断向量表基址:设置中断描述符表寄存器(IDTR),指向中断向量表的基地址和大小。
示例代码
c
深色版本
VOID InitializeInterruptVectorTable() {
// 初始化中断向量表
IDT_ENTRY idt[IDT_SIZE]; // 假设IDT_SIZE为256
// 清零中断向量表
memset(idt, 0, sizeof(idt));
// 设置中断处理程序地址
SetIdtGate(idt, 0x20, (UINT32)TimerInterruptHandler, 0x08, 0x8E); // 设置定时器中断处理程序
SetIdtGate(idt, 0x21, (UINT32)KeyboardInterruptHandler, 0x08, 0x8E); // 设置键盘中断处理程序
// 设置中断描述符表寄存器IDTR
IDT_POINTER idtr;
idtr.Limit = sizeof(idt) - 1;
idtr.Base = (UINT32)idt;
LoadIdtr(idtr);
// 开启中断
EnableInterrupts();
}
VOID SetIdtGate(IDT_ENTRY *idt, UINT8 vector, UINT32 handler, UINT16 selector, UINT8 flags) {
// 设置中断门描述符
idt[vector].OffsetLow = handler & 0xFFFF;
idt[vector].Selector = selector;
idt[vector].Always0 = 0;
idt[vector].Flags = flags;
idt[vector].OffsetHigh = (handler >> 16) & 0xFFFF;
}
VOID LoadIdtr(IDT_POINTER idtr) {
// 加载中断描述符表寄存器IDTR
__asm__ __volatile__(
"lidt %0"
: : "m"(idtr)
);
}
VOID EnableInterrupts() {
// 开启中断
__asm__ __volatile__("sti");
}
3. 编写中断处理程序
中断处理程序是处理特定中断事件的函数。每个中断处理程序都需要遵循一定的格式,并且通常会调用相应的硬件驱动程序来处理中断事件。
示例代码
c
深色版本
VOID TimerInterruptHandler() {
// 处理定时器中断
Print(L"Timer Interrupt Handler Called\n");
// 发送EOI(End of Interrupt)信号给PIC(Programmable Interrupt Controller)
SendEoiToPic(0x20);
// 返回中断处理
__asm__ __volatile__("iret");
}
VOID KeyboardInterruptHandler() {
// 处理键盘中断
Print(L"Keyboard Interrupt Handler Called\n");
// 读取键盘扫描码
UINT8 scan_code = Inb(0x60);
// 发送EOI信号给PIC
SendEoiToPic(0x21);
// 返回中断处理
__asm__ __volatile__("iret");
}
VOID SendEoiToPic(UINT8 irq) {
// 发送EOI信号给PIC
if (irq >= 8) {
Outb(0xA0, 0x20); // 发送EOI给从PIC
}
Outb(0x20, 0x20); // 发送EOI给主PIC
}
UINT8 Inb(UINT16 port) {
// 从端口读取字节
UINT8 value;
__asm__ __volatile__(
"inb %1, %0"
: "=a"(value)
: "d"(port)
);
return value;
}
VOID Outb(UINT16 port, UINT8 value) {
// 向端口写入字节
__asm__ __volatile__(
"outb %0, %1"
: : "a"(value), "d"(port)
);
}
4. 编写中断处理模块
现在,我们将把这些初始化步骤和中断处理程序整合到一个完整的BIOS模块中。
-
创建项目目录:
- 在
edk2目录下创建一个新的目录,例如InterruptHandlingModule。 - 在
InterruptHandlingModule目录下创建InterruptHandlingModule.inf文件,内容如下: ini深色版本
[Defines] INF_VERSION = 0x00010005 BASE_NAME = InterruptHandlingModule FILE_GUID = 12345678-1234-1234-1234-1234567890AB MODULE_TYPE = UEFI_DRIVER VERSION_STRING = 1.0 UNIVERSAL = TRUE PROCESSOR = IA32 X64 [Sources] InterruptHandlingModule.c [Packages] MdePkg/MdePkg.dec MdeModulePkg/MdeModulePkg.dec [LibraryClasses] UefiLib UefiDriverEntryPoint [Protocols] gEfiShellProtocolGuid [FeaturePcd] gEfiMdePkgTokenSpaceGuid.PcdDebugPrintErrorLevel|0x8000000F
- 在
-
编写模块代码:
- 在
InterruptHandlingModule目录下创建InterruptHandlingModule.c文件,内容如下: c深色版本
#include <Uefi.h> #include <Library/BaseLib.h> #include <Library/UefiLib.h> #include <Library/UefiBootServicesTableLib.h> #define IDT_SIZE 256 typedef struct { UINT16 OffsetLow; UINT16 Selector; UINT8 Always0; UINT8 Flags; UINT16 OffsetHigh; } IDT_ENTRY; typedef struct { UINT16 Limit; UINT32 Base; } IDT_POINTER; VOID InitializeInterruptVectorTable() { // 初始化中断向量表 IDT_ENTRY idt[IDT_SIZE]; // 假设IDT_SIZE为256 // 清零中断向量表 memset(idt, 0, sizeof(idt)); // 设置中断处理程序地址 SetIdtGate(idt, 0x20, (UINT32)TimerInterruptHandler, 0x08, 0x8E); // 设置定时器中断处理程序 SetIdtGate(idt, 0x21, (UINT32)KeyboardInterruptHandler, 0x08, 0x8E); // 设置键盘中断处理程序 // 设置中断描述符表寄存器IDTR IDT_POINTER idtr; idtr.Limit = sizeof(idt) - 1; idtr.Base = (UINT32)idt; LoadIdtr(idtr); // 开启中断 EnableInterrupts(); } VOID SetIdtGate(IDT_ENTRY *idt, UINT8 vector, UINT32 handler, UINT16 selector, UINT8 flags) { // 设置中断门描述符 idt[vector].OffsetLow = handler & 0xFFFF; idt[vector].Selector = selector; idt[vector].Always0 = 0; idt[vector].Flags = flags; idt[vector].OffsetHigh = (handler >> 16) & 0xFFFF; } VOID LoadIdtr(IDT_POINTER idtr) { // 加载中断描述符表寄存器IDTR __asm__ __volatile__( "lidt %0" : : "m"(idtr) ); } VOID EnableInterrupts() { // 开启中断 __asm__ __volatile__("sti"); } VOID TimerInterruptHandler() { // 处理定时器中断 Print(L"Timer Interrupt Handler Called\n"); // 发送EOI(End of Interrupt)信号给PIC(Programmable Interrupt Controller) SendEoiToPic(0x20); // 返回中断处理 __asm__ __volatile__("iret"); } VOID KeyboardInterruptHandler() { // 处理键盘中断 Print(L"Keyboard Interrupt Handler Called\n"); // 读取键盘扫描码 UINT8 scan_code = Inb(0x60); // 发送EOI信号给PIC SendEoiToPic(0x21); // 返回中断处理 __asm__ __volatile__("iret"); } VOID SendEoiToPic(UINT8 irq) { // 发送EOI信号给PIC if (irq >= 8) { Outb(0xA0, 0x20); // 发送EOI给从PIC } Outb(0x20, 0x20); // 发送EOI给主PIC } UINT8 Inb(UINT16 port) { // 从端口读取字节 UINT8 value; __asm__ __volatile__( "inb %1, %0" : "=a"(value) : "d"(port) ); return value; } VOID Outb(UINT16 port, UINT8 value) { // 向端口写入字节 __asm__ __volatile__( "outb %0, %1" : : "a"(value), "d"(port) ); } VOID MyInitializationFunction() { InitializeInterruptVectorTable(); Print(L"Interrupt Handling Initialization Complete\n"); } EFI_STATUS EFIAPI UefiMain ( IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *SystemTable ) { MyInitializationFunction(); return EFI_SUCCESS; }
- 在
-
编译模块:
- 打开命令提示符或终端。
- 导航到
edk2目录。 - 运行以下命令编译模块: sh
深色版本
build -p InterruptHandlingModule/InterruptHandlingModule.inf
-
运行模块:
- 编译成功后,你可以在
Build目录下找到生成的.efi文件。例如:深色版本
Build\InterruptHandlingModule\DEBUG_VS2019x86\X64\InterruptHandlingModule.efi - 使用QEMU或其他虚拟机工具运行生成的模块,验证其功能。例如,使用QEMU: sh
深色版本
qemu-system-x86_64 -bios path\to\OVMF.fd -m 2G -hda path\to\mydisk.img -drive file=path\to\Build\InterruptHandlingModule\DEBUG_VS2019x86\X64\InterruptHandlingModule.efi,format=raw
- 编译成功后,你可以在
作业
-
阅读材料:
- 阅读Intel的官方文档,了解中断处理和中断向量表的详细信息。
- 阅读EDK II的官方文档,了解模块开发的最佳实践。
-
实践任务:
- 编写一个更复杂的中断处理模块,例如支持多个硬件设备的中断处理。
- 编译并测试该模块,记录下遇到的问题和解决方法。
明天的任务
明天我们将继续深入探讨设备初始化,了解如何在BIOS中初始化各种硬件设备,如显卡、网卡等。准备好迎接新的挑战吧!

被折叠的 条评论
为什么被折叠?



