如何设置IDT
IDT 中断描述符表定义
中断描述符表简单来说说是定义了发生中断/异常时,CPU按这张表中定义的行为来处理对应的中断/异常。
#define IDT_ENTRIES 256
gate_desc idt_table[IDT_ENTRIES] __page_aligned_bss;
从上面我们可以知道,其包含了256项,它是一个gate_desc
的数据,其下标0-256就表示中断向量,gate_desc
我们在下面马上介绍。
中断描述符项定义
- 当中断发生,cpu获取到中断向量后,查找IDT中断描述符表得到相应的中断描述符,再根据中断描述符记录的信息来作权限判断,运行级别转换,最终调用相应的中断处理程序;
- 这里涉及到Linux kernel的分段式内存管理,我们这里不详细展开,有兴趣的同学可以自行学习。如下简述之:
- 我们知道CPU只认识逻辑地址,逻辑地址经分段处理转换成线性地址,线性地址经分页处理最终转换成物理地址,这样就可以从内存中读取了;
- 逻辑地址你可以简单认为就是CPU执行代码时从CS(代码段寄存器) : IP (指令计数寄存器)中加载的代码,实际上通过CS可以得到逻辑地址的基地址,再加上IP这个相对于基地址的偏移量,就得到真正的逻辑地址;
- CS寄存器16位,它不会包含真正的基地址,它一般被称为
段选择子
,包括一个index索引,指向GDT
或LDT
的一项;一个指示位,指示index索引是属于GDT
还是LDT
; 还有CPL
, 表明当前代码运行权限; GDT
: 全局描述符表,每一项记录着相应的段基址,段大小,段的访问权限DPL
等,到这里终于可以获取到段基地址了,再加上之前IP
寄存器里存放的偏移量,真正的逻辑地址就有了。- 附上简图:
idt2.jpg
- 我们先看中断描述符的定义:
struct gate_struct {
u16 offset_low;
u16 segment;
struct idt_bits bits;
u16 offset_middle;
#ifdef CONFIG_X86_64
u32 offset_high;
u32 reserved;
#endif
} __attribute__((packed));
其中:
offset_high
,offset_middle
和offset_low
合起来就是中断处理函数地址的偏移量;segment
就是相应的段选择子,根据它在GDT
中查找可以最终获取到段基地址;bits
是该中断描述符的一些属性值:
struct idt_bits {
u16 ist : 3,
zero : 5,
type : 5,
dpl : 2,
p : 1;
} __attribute__((packed));
ist
表示此中断处理函数是使用pre-cpu的中断栈,还是使用IST的中断栈;
type
表示所中断是何种类型,目前有以下四种:
enum {
GATE_INTERRUPT = 0xE, //中断门
GATE_TRAP = 0xF, // 陷入门
GATE_CALL = 0xC, // 调用门
GATE_TASK = 0x5, // 任务门
};
门
的概念这里主要用作权限控制,我们从一个区域进到另一个区域需要通过一扇门,有门禁权限才可以通过,因此 dpl
就是这个权限,实际中我们