BIOS下的中断开发

一、简介

X86架构中,中断 (Interrupts) 是一种重要的系统机制,用于处理硬件事件和软件请求,以及响应异常情况。本文将探索学习中断的概念、中断向量和中断描述符表的作用、中断的执行机制等。

二、基本概念

中断是由硬件设备、软件请求或异常条件引发的异步事件。当发生中断时,CPU会暂停当前正在执行的指令流程,并跳转到预定义的中断处理程序来处理中断事件。处理程序执行完毕后,CPU将恢复之前的上下文并继续原先的执行。

2.1 中断的类型

中断可分为以下几种类型,每种类型都有不同的用途。

外部中断(External Interrupts):外部中断是由外部设备触发的中断。例如,键盘输入、鼠标事件、硬件设备请求等都可以引发外部中断。外部中断通过中断控制器(例如8259A)来管理和传递,处理器会暂停当前正在执行的任务,根据中断向量号找到相应的中断门描述符,然后跳转到相应的中断处理程序。

软件中断(Software Interrupts):软件中断是由程序中的指令触发的中断,通常使用特定的中断指令(如INT指令)来产生中断。软件中断常用于实现系统调用(System Call)或在程序中主动请求中断服务。通过指令产生软件中断,处理器会根据中断向量号在中断描述符表(IDT,Interrupt Descriptor Table)中找到对应的中断门描述符,然后执行相应的中断处理程序。

可屏蔽硬件中断(Maskable Hardware Interrupts):可屏蔽硬件中断是一种可由软件屏蔽的硬件中断。可以使用STI(Set Interrupt Flag)指令设置中断屏蔽标志为允许中断,使用CLI(Clear Interrupt Flag)指令将中断屏蔽标志设置为屏蔽中断。允许屏蔽中断的机制使得操作系统或应用程序可以在必要时延迟或阻止中断处理,以确保系统的稳定性和关键任务的执行。

NMI中断(Non-Maskable Interrupts):NMI中断是一种特殊的中断类型,它是一种非可屏蔽中断。NMI中断是由硬件级别的故障或异常引起的,不受中断屏蔽标志的影响,处理器会立即响应NMI中断,并跳转到相应的NMI中断处理程序。

2.2 中断描述符(IDT)

中断描述符用于指示处理器如何处理不同类型的中断、异常和任务切换。每个中断描述符包含有关中断处理程序或任务的入口地址、特权级别、段选择子等信息。中断描述符包含三种门描述符:中断门描述符(Interrupt Gate Descriptor)、陷阱门描述符(Trap Gate Descriptor)和任务门描述符(Task Gate Descriptor),这三种门描述符的主要区别在于它们对中断的响应方式和处理程序的特权级别要求。

中断门描述符(Interrupt Gate Descriptor):用于处理可屏蔽中断(可被其他中断打断)。当处理器从用户态切换到内核态时,中断门描述符会将处理器特权级别提升(通常从用户态提升到内核态),并禁用其他可屏蔽中断的响应。这样能确保在处理当前中断时不会被其他中断打断。

陷阱门描述符(Trap Gate Descriptor):用于处理陷阱(Trap)指令产生的中断和异常,以及对特定调试功能的支持。与中断门描述符不同的是,陷阱门描述符不会禁用其他可屏蔽中断的响应,即在处理陷阱时,其他中断仍然能被响应。

任务门描述符(Task Gate Descriptor):用于实现任务切换。任务门描述符与任务状态段(TSS, Task State Segment)关联,用于保存和恢复任务的上下文信息。任务门描述符在任务切换时,将处理器从当前任务切换到另一个任务,并将任务的上下文信息加载到TSS中。

三种门描述符结构(32位模式)

Offset:中断处理程序的偏移量,表示中断处理程序在代码段中的位置。在32位模式下,占用32位,分为两个16位部分。
Selector:段选择子,指向包含中断处理程序的代码段。

P (Present):存在标志,用于指示该表项是否有效。如果P位为1,表示该表项有效;如果为0,表示该表项无效,中断将不会被处理。

DPL (Descriptor Privilege Level):描述符特权级别,表示表项的访问权限。DPL值越低,权限越高。

bit12:存储段标志,用于指示段描述符类型。对于门描述符,这个位通常被设置为0,表示该门描述符引用的是系统段描述符。

D:门描述符大小,32bit为1,16bit为0。

Type(bit8-10):中断门的类型,表示表项是中断门描述符、陷阱门描述符还是任务门描述符。

2.3 IDTR Register

IDTR(Interrupt Descriptor Table Register)是Intel x86架构中的一个特殊寄存器,用于存储中断描述符表(IDT,Interrupt Descriptor Table)的基址和限长信息。IDT是一个用于管理中断和异常处理程序的数据结构,其中存储着每个中断向量号对应的中断门描述符或陷阱门描述符。

IDTR是48位的,用于存储IDT的基址和限长信息。

IDTR寄存器包含以下两个重要的字段:
IDT Base Address:32bit IDT的基址,用于指示IDT在内存中的起始地址。IDT是一个连续的内存区域,Base字段指示了IDT的起始位置。

IDT Limit:IDT的限长,表示IDT的大小。它指示了IDT能够容纳的表项数量。Limit字段是16位,表示IDT最多可以存储2^16个表项。

根据IDT(Interrupt Descriptor Table)的基址(BASE)和限长(Limit),可以计算出IDT在内存中的起始地址和结束地址。IDT是一个连续的内存区域,其中存储了多个门描述符(Gate Descriptor),每个门描述符占用8字节(64位)。

IDT的结束地址 = BASE + LIMIT * 8 – 1。其中,LIMIT * 8表示IDT中表项的总字节数,再减1是因为地址是从0开始计数的。需要注意的是,IDT的LIMIT字段表示的是IDT的大小(表项数量),而不是字节数。所以在计算结束地址时,需要将LIMIT乘以8来得到IDT的总字节数。

2.4 LIDT和SIDT

LIDT(Load Interrupt Descriptor Table)和SIDT(Store Interrupt Descriptor Table)是与IDTR(Interrupt Descriptor Table Register)寄存器相关的指令,用于加载和存储中断描述符表(IDT)的基址和限长信息。

LIDT指令用于将IDT的基址和限长信息加载到IDTR寄存器中。它的操作数是一个内存地址,该地址指向一个6字节(在32位模式下)或10字节(在64位模式下)的内存数据结构,其中包含IDT的基址和限长。LIDT指令将这些信息加载到IDTR寄存器中,从而告诉处理器IDT在内存中的位置和大小。

SIDT指令用于将IDTR寄存器中的IDT的基址和限长信息存储到指定的内存地址中。它的操作数是一个内存地址,SIDT指令将IDTR寄存器中的内容存储到该地址指向的内存位置。这样做可以在需要的时候,保存IDT的基址和限长信息,以便稍后恢复IDT的状态。

2.5 中断向量

中断向量是在计算机系统中用于标识不同中断和异常的唯一编号,它也称为中断向量号(Interrupt Vector Number),通常用一个整数来表示,范围是0~255。

中断向量号在中断描述符表(IDT,Interrupt Descriptor Table)中用于索引相应的门描述符。每个中断向量号对应一个特定的中断类型或异常情况,处理器通过中断向量号可以快速定位到对应的中断门描述符或陷阱门描述符,从而跳转到相应的中断处理程序或异常处理程序。

在不同的工作模式下,中断机制的特性和处理方式可能会有所不同。在保护模式下,中断向量号用于定位中断门描述符,提供了更灵活的中断处理和内存保护机制。而在实模式下,中断向量号用于定位中断处理程序的入口地址,内存访问受到20位地址的限制,只能访问1MB的内存空间。

三、执行机制

中断的执行机制涉及以下步骤:

中断请求:中断可以由外部设备发起,例如键盘、鼠标或网络卡的输入信号,或者由CPU内部的异常条件触发,如除零错误或页面故障。

中断处理程序查找:当中断发生时,CPU会根据中断向量号 (Interrupt Vector Number) 在IDT中查找相应的中断门描述符 (Interrupt Gate Descriptor)。

中断处理程序执行:根据中断门描述符中的段选择子和偏移量,CPU会构造中断门并跳转到中断处理程序的实际地址,从而开始执行中断处理程序。

上下文保存和恢复:在执行中断处理程序之前,CPU会保存当前程序的上下文,包括寄存器内容、代码段选择子和栈指针等。处理程序执行完毕后,CPU会从保存的上下文中恢复,并继续执行被中断的程序。