《异常处理类中断服务程序挂接》

Linux0.11 版本的 set_trap_gate 宏分析
《异常处理类中断服务程序挂接》
出处:http://wenku.baidu.com/link?url=5Y1uhn9-Ut1JJRib_iKSEVfieHyWHNnQStuOyR6sPXrLXRUvvadrS2zoQ7BMDYuy7Dl8GUTVYGps9nU0KuCqL9r9o2Sg6l4DrFl57P-UISS

比较老的版本的 linux,如 linux0.11,在 kernel/main.c 中的 main 函数相当于新
版本的 linux start_kernel 函数,从该函数开始则会开始 linux 的内核工作,其中看到
trap_init 函数中, 其实是在设置异常 (中断) 程序初始化子程序, 设置相应的中断调用门 (中断调用),该函数定义如下:

void main(void)
{
……
trap_init();
……
}

void trap_init(void)
{
int i;

set_trap_gate(0,&divide_error);
set_trap_gate(1,&debug);
set_trap_gate(2,&nmi);
set_system_gate(3,&int3);   /* int3-5 can be called from all */
set_system_gate(4,&overflow);
set_system_gate(5,&bounds);
set_trap_gate(6,&invalid_op);
set_trap_gate(7,&device_not_available);
set_trap_gate(8,&double_fault);
set_trap_gate(9,&coprocessor_segment_overrun);
set_trap_gate(10,&invalid_TSS);
set_trap_gate(11,&segment_not_present);
set_trap_gate(12,&stack_segment);
set_trap_gate(13,&general_protection);
set_trap_gate(14,&page_fault);
set_trap_gate(15,&reserved);
set_trap_gate(16,&coprocessor_error);
for (i=17;i<48;i++)
    set_trap_gate(i,&reserved);
set_trap_gate(45,&irq13);
outb_p(inb_p(0x21)&0xfb,0x21);
outb(inb_p(0xA1)&0xdf,0xA1);
set_trap_gate(39,&parallel_interrupt);

}

define set_trap_gate(n,addr) \

_set_gate(&idt[n],15,0,addr)

define _set_gate(gate_addr,type,dpl,addr) \

asm (“movw %%dx,%%ax\n\t” \
“movw %0,%%dx\n\t” \
“movl %%eax,%1\n\t” \
“movl %%edx,%2” \
: \
: “i” ((short) (0x8000+(dpl<<13)+(type<<8))), \
“o” (((char ) (gate_addr))), \
“o” ((4+(char ) (gate_addr))), \
“d” ((char *) (addr)),”a” (0x00080000))

首先,这里用到了 gcc 的内联汇编,在此进行下简要讲解,gcc 的内联汇编的基本表示 如下: asm (汇编语句
输出寄存器
输入寄存器
会被修改的寄存器
)
这里面除第一行外,后面带“:”的如果不用都是可以省掉的,其中“汇编语句”即为
需要执行的汇编代码序列, “输出寄存器”指定需要使用哪些寄存器作为输出参数使用,一般对应于 C 语言的一个表达式值或者是内存地址, “输入寄存器”指明在执行汇编代码时,使用哪些寄存器来作为输入参数使用,也是对应的 C 语言的某数值或者内存地址, “会被修改的寄存器” 指明这些寄存器是没有在输入输出寄存器中列出, 但是在汇编语句中会被修改的寄存器的值,已告知 gcc 不能加载原先的值,需要重新加载。另外,在汇编块中会见到%0,%1,%2,……这些对应的就是输出寄存器和输入寄存器的非寄存器对应的参数值,按顺序依次对应%0,%1….,可以参考后面的具体说明,这里面会看到一些以引号括起来的字符,如“i”, “o”, “d”, “a”, 这些是寄存器加载代码,以此指明如何加载输入输出项,现在将常用的加载代码列出:

这里写图片描述

其次,这里涉及到 x86 的中断门和陷阱门的知识,在此就不详述了,感兴趣的同学可以
到网上查找,相关介绍很多, set_trap_gate 主要是设置陷阱门,从 intel 的手册中截出
各个门的描述符结构定义图:
这里写图片描述

对于陷阱门,也即 Trap Gate,可以看出,0-15bit 存放的是回调函数地址的低 16 位,16-31bit
对应的是段选择符(在 linux0.11 版本中代码段选择符为 0x8,数据段选择符为 0x10,而在新的 linux 版本中有所不同) ,32-36bit 是 resverved,38-40bit 对于 IDT 来说都是设为 0 的,40-44bit 是 type 用来确认是哪种门, 比如对于 Trap Gate 对应的就是 0 1 (D=1) 1 1 1, 45-46bit对应的是 DPL(Descriptor Privilege Level) ,这里就是如雷贯耳的 Ring0 或者 Ring3,在内核态自然是 Ring0,所以应该填 1,47bit 是 P,只有 P=1 时才有效,因此应该填 1,48-63bit存放的是回调函数地址的高 16 位。

至此, 可以针对 set_trap_gate 宏进行分析, 通过内联汇编块可以看出, 宏_set_gate
没有输出寄存器,也没有除输入寄存器定义的寄存器外的其它寄存器被修改,

define set_trap_gate(n,addr) \

_set_gate(&idt[n],15,0,addr)
参数 n 对应于代码中定义的 idt 数组表的索引值,addr 即为注册的回调函数的地址,15
表明了上面提到的 40-44bit,type 属于 Trap Gate,0 表明了上面说的 45-46bit,即处理内核态, Ring0,下面进入内联汇编块中,先看输入寄存器定义:
%0 - “i” ((short) (0x8000+(dpl<<13)+(type<<8))),该输入项说明是立即数,
0x8000 对应的上面 Trap Gate 的 47bit(P),dpl<<13 将 dpl(在此是 ring0)填入到
上图的 45-46bit,type<<8 将 type 填入到 40-44bit,在此是陷阱门应为 0xF;
%1 - “o” (((char ) (gate_addr))), 该输入项说明是使用内存地址并且可以加
偏移值,这个对应的是 idt[n]的低 4 个字节;
%2 - “o” ((4+(char ) (gate_addr))), 该输入项说明是使用内存地址并且可以
加偏移值,这个对应的是 idt[n]的高 4 个字节;
“d” ((char *) (addr)),”a” (0x00080000)),说明 edx 存放回调函数地址,eax
存放陷阱门描述符中的 16-31bit(Segment Selector)。

这样看的话汇编语句应该就很清晰了:
“movw %%dx,%%ax\n\t” – 将回调函数的低16位放入描述符的0-15bit,放入ax,这
样与原本的eax的高16位(0x0008)组合,组成了描述符的低4个字节内容;
“movw %0,%%dx\n\t” - 填写描述符的32-47bit,放入dx,这样与原本的edx的高
16位(回调函数的高16位地址)组合,组成了描述符的高4个字节内容;
“movl %%eax,%1\n\t” - 将描述符的低4字节放入idt[n]的低4字节;
“movl %%edx,%2” - 将描述符的高 4 节放入 idt[n]的高 4 字节。

至此该函数分析完成,其实就是在填写陷阱门的描述符,上面的分析都是基于 x86 的机制
进行的,arm 方面的与此还是有不同的,不过在看 linux 内核书籍的时候遇到此处也是有分析的必要的。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值