LINUX0.11 main 函数中traps_init()作用研究
目的:研究linux0.11系统初始化时,执行traps_init()函数后,相关的捕获函数如何与中断表述符表进行关联?
试验环境:linux-0.11-devel-050518 (在oldlinux网站上可以下载)。其中包括bochs虚拟机,和带编译环境的linux0.11操作系统。运行linux-0.11-devel-050518中的bochsrc-hd.bxrc文件,
即可以在bochs中运行linux0.11系统,该系统中已经将linux0.11的内核进行了编译,生成的文件放在usr/src/linux/boot, usr/src/linux/kernel等目录下。
资料准备:1、上面系统中已经编译好的bootsect,setup,head.o文件。
2、改写main.c 和 traps.c,改写的文件如下:
main.c
long user_stack[2048];
struct
{
long *a;
short b;
}stack_start = {&user_stack[2048], 0x10};
void main(void)
{
trap_init();
for(;;)
{
;
}
}
int printk(const char *fmt, ...)
{
return 0;
}
traps.c
typedef struct desc_struct
{
unsigned long a, b;
}desc_table[256];
extern desc_table idt;
#define move_to_user_mode() /
__asm__ ("movl %%esp,%%eax/n/t" /
"pushl $0x17/n/t" /
"pushl %%eax/n/t" /
"pushfl/n/t" /
"pushl $0x0f/n/t" /
"pushl $1f/n/t" /
"iret/n" /
"1:/tmovl $0x17,%%eax/n/t" /
"movw %%ax,%%ds/n/t" /
"movw %%ax,%%es/n/t" /
"movw %%ax,%%fs/n/t" /
"movw %%ax,%%gs" /
:::"ax")
#define sti() __asm__ ("sti"::)
#define cli() __asm__ ("cli"::)
#define nop() __asm__ ("nop"::)
#define iret() __asm__ ("iret"::)
#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))
#define set_intr_gate(n,addr) /
_set_gate(&idt[n],14,0,addr)
#define set_trap_gate(n,addr) /
_set_gate(&idt[n],15,0,addr)
#define set_system_gate(n,addr) /
_set_gate(&idt[n],15,3,addr)
#define _set_seg_desc(gate_addr,type,dpl,base,limit) {/
*(gate_addr) = ((base) & 0xff000000) | /
(((base) & 0x00ff0000)>>16) | /
((limit) & 0xf0000) | /
((dpl)<<13) | /
(0x00408000) | /
((type)<<8); /
*((gate_addr)+1) = (((base) & 0x0000ffff)<<16) | /
((limit) & 0x0ffff); }
#define _set_tssldt_desc(n,addr,type) /
__asm__ ("movw $104,%1/n/t" /
"movw %%ax,%2/n/t" /
"rorl $16,%%eax/n/t" /
"movb %%al,%3/n/t" /
"movb $" type ",%4/n/t" /
"movb $0x00,%5/n/t" /
"movb %%ah,%6/n/t" /
"rorl $16,%%eax" /
::"a" (addr), "m" (*(n)), "m" (*(n+2)), "m" (*(n+4)), /
"m" (*(n+5)), "m" (*(n+6)), "m" (*(n+7)) /
)
#define set_tss_desc(n,addr) _set_tssldt_desc(((char *) (n)),addr,"0x89")
#define set_ldt_desc(n,addr) _set_tssldt_desc(((char *) (n)),addr,"0x82")
#define get_seg_byte(seg,addr) ({ /
register char __res; /
__asm__("push %%fs;mov %%ax,%%fs;movb %%fs:%2,%%al;pop %%fs" /
:"=a" (__res):"0" (seg),"m" (*(addr))); /
__res;})
#define get_seg_long(seg,addr) ({ /
register unsigned long __res; /
__asm__("push %%fs;mov %%ax,%%fs;movl %%fs:%2,%%eax;pop %%fs" /
:"=a" (__res):"0" (seg),"m" (*(addr))); /
__res;})
#define _fs() ({ /
register unsigned short __res; /
__asm__("mov %%fs,%%ax":"=a" (__res):); /
__res;})
void divide_error(void)
{
;
}
static void die(char * str,long esp_ptr,long nr)
{
;
}
void do_divide_error(long esp, long error_code)
{
die("divide error",esp,error_code);
}
void trap_init(void)
{
set_trap_gate(0,÷_error);
}
3、已经制作好的build工具。
下面进行研究:
第一步:编译main.c 和traps.c文件,链接head.o ,main.o,traps.o文件
第二步:将system.map导出,为后续分析作准备。
system.map:
Allocating common _user_stack: 2000 at 7008
Files:
head.o text 0(64b8), data 7000(0), bss 7008(0) hex
main.o text 64b8(1c), data 7000(8), bss 7008(0) hex
traps.o text 64d4(58), data 7008(0), bss 7008(0) hex
Global symbols:
_tmp_floppy_area: 0x5000
_stack_start: 0x7000
__edata: 0x7008
_pg_dir: 0x0
_printk: 0x64c8
__etext: 0x7000
_divide_error: 0x64d4
_user_stack: 0x7008
_do_divide_error: 0x64f0
__end: 0x9008
_etext: 0x7000
_trap_init: 0x6508
_main: 0x64b8
_edata: 0x7008
_end: 0x9008
_gdt: 0x5cb8
_idt: 0x54b8
Local symbols of head.o:
startup_32: 0x0
setup_idt: 0x6f
setup_gdt: 0x9f
check_x87: 0x5a
after_page_tables: 0x5400
ignore_int: 0x5428
rp_sidt: 0x8c
idt_descr: 0x54aa
gdt_descr: 0x54b2
pg0: 0x1000
pg1: 0x2000
pg2: 0x3000
pg3: 0x4000
setup_paging: 0x5450
int_msg: 0x5414
Local symbols of main.o:
gcc_compiled.: 0x64b8
Local symbols of traps.o:
gcc_compiled.: 0x64d4
_die: 0x64dc
第三步:使用build工具将相关文件build成image文件,该文件可以算操作系统文件。
第四步:将生成的iamge文件以写磁盘命令写到软盘中,制成系统盘。
下面回到主题:研究linux0.11系统初始化时,执行traps_init()函数后,相关的捕获函数如何与中断表述符表进行关联?
基础知识准备:
中断描述符表的建立:
中断描述符表(IDT)的创建代码在boot/head.s中,与全局描述符表的创建类似,内核执行lidt idt_descr指令完成创建工作,全局变量idt_descr的定义如下:
idt_descr:
.word 256*8-1 # idt contains 256 entries
.long _idt
_idt: .fill 256,8,0 # idt is uninitialized
lidt指令为6字节操作数,它将_idt的地址加载进idtr寄存器,IDT被设置为包含256个8字节表项的描述符表。
中断描述符表的初始化工作主要通过宏_set_get来完成,它定义于traps中.如下:
#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))
/*设置中断门函数,特权级0,类型386中断门*/
#define set_intr_gate(n,addr) /
_set_gate(&idt[n],14,0,addr)
/*设置陷阱门函数,特权级0,类型386陷阱门*/
#define set_trap_gate(n,addr) /
_set_gate(&idt[n],15,0,addr)
/*设置系统调用函数,特权级3,类型386陷阱门*/
#define set_system_gate(n,addr) /
_set_gate(&idt[n],15,3,addr)
内核将用这些宏初始化IDT表,代码如下:
set_trap_gate(0,÷_error);
每个中断向量号具体意义这里不做说明,有兴趣的同志可以参考清华大学出版社出版的《保护方式下的80386及其编程》和赵炯博士的《Linux内核完全注释》;中断调用的具体过程将在后面的例子中详细分析。现在我们关心的是初始化完毕的IDT,调试查看这张表的内容,选取0x0号中断作为例子。通过查看System.map文件可知:0x0号中断调用的divide_error函数地址为_divide_error: 0x64d4。Map文件中_trap_init: 0x6508,因此我们在_trap_ini函数地址0x6508处设置断点,启动bochsdgb进行调试,命令行如下:
门描述符具有如下形式:
因此调试信息显示,0x0号中断描述符中断调用地址为0x0008:0x000064d4,是一个特权级为0的386陷阱门,这和在system.map中_divide_error: 0x64d4一致。
注:以上分析参考了《实例分析Linux0.11内核中断机制 》这篇文章。