您的位置:首页 > 运维架构 > Linux

LINUX0.11 main 函数中traps_init()作用研究

2008-03-24 23:35 447 查看
 
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
,14,0,addr)
 

#define set_trap_gate(n,addr) /
       _set_gate(&idt
,15,0,addr)
 

#define set_system_gate(n,addr) /
       _set_gate(&idt
,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
,14,0,addr)

/*设置陷阱门函数,特权级0,类型386陷阱门*/

#define set_trap_gate(n,addr) /

       _set_gate(&idt
,15,0,addr)

/*设置系统调用函数,特权级3,类型386陷阱门*/

#define set_system_gate(n,addr) /

_set_gate(&idt
,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内核中断机制 》这篇文章。

 
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息