笔记二:从动态和静态,跟踪Minix中断过程

Ricky Zhang
rickycheung@21cn.com
2005-2-14

1、实验目的:
了解Minix如何封装中断调用,并如本身内核消息传递机制融合。

2、实验目标:
动态上跟踪:在Bochs上,对系统的键盘中断调用代码/kernel/Mpx386.s的hwint_master01加断点,续步跟踪了解。

静态上跟踪:与系统中断有关源代码有,/kernel/Mpx386.s,/kernel/proc.c

3、实验步骤:
动态跟踪笔记:

一、把Minix的内核映像(/minix/XXX)上传到windows上,用任何反编译器反汇编(Debug也可以,但我用了W32Dasm)。

用sti为关键字找到/kernel/Mpx386.s的hwint_master01代码段,源码中在中断汇编代码使用了宏

----------------/kernel/Mpx386.s的hwint_master01代码段-------------------------------
!*===========================================================================*
!*    hwint00 - 07         *
!*===========================================================================*
! Note this is a macro, it looks like a subroutine.
#define hwint_master(irq) /
 call save   /* save interrupted process state */;/
 inb INT_CTLMASK          ;/
 orb al, [1<<irq]          ;/
 outb INT_CTLMASK  /* disable the irq    */;/
 movb al, ENABLE          ;/
 outb INT_CTL   /* reenable master 8259    */;/
 sti    /* enable interrupts    */;/
 push irq   /* irq      */;/
 call (_irq_table + 4*irq) /* eax = (*irq_table[irq])(irq)   */;/
 pop ecx           ;/
 cli    /* disable interrupts    */;/
 test eax, eax  /* need to reenable irq?   */;/
 jz 0f           ;/
 inb INT_CTLMASK          ;/
 andb al, ~[1<<irq]          ;/
 outb INT_CTLMASK  /* enable the irq    */;/
0: ret    /* restart (another) process      */

! Each of these entry points is an expansion of the hwint_master macro
 .align 16
_hwint00:  ! Interrupt routine for irq 0 (the clock).
 hwint_master(0)

 .align 16
_hwint01:  ! Interrupt routine for irq 1 (keyboard)
 hwint_master(1)
--------------------------------------------------------------------------------------------------

------------------------------------对应/kernel/Mpx386.s的hwint_master01的内核映像反汇编的结果
:000002F1 E8CB020000              call 000005C1
:000002F6 E421                    in al, 21
:000002F8 0C02                    or al, 02
:000002FA E621                    out 21, al
:000002FC B020                    mov al, 20
:000002FE E620                    out 20, al
:00000300 FB                      sti
:00000301 6A01                    push 00000001
:00000303 FF15C86C0000            call dword ptr [00006CC8]
:00000309 59                      pop ecx
:0000030A FA                      cli
:0000030B 85C0                    test eax, eax
:0000030D 7406                    je 00000315
:0000030F E421                    in al, 21
:00000311 24FD                    and al, FD
:00000313 E621                    out 21, al
:00000315 C3                      ret


:00000316 00000000000000000000    BYTE 10 DUP(0)


:00000320 00                      BYTE 0
---------------------------------------------------------------------------------------------------------------------------------

2、从笔记一中提到,由Secondary Boot把minix的映像文件(在/minix下),装载到内存物理0x00800地址上(源码在/boot/boot.h下定义'#define MINIXPOS       0x00800L /* Minix is loaded here (rounded up towards)')

3、用bochs的debug功能bochsdbg,添加一个物理地址的断点
因为minix映象在内存的物理开始地址是0x00800,而hwint_master01的偏移是0x000002F1 (里面有200h的a.out.h的头,因些要减去200h),所以在内存中hwint01的物理开始地址是0x00800+0x002F1-200h-1h=0x8f0
在bochs的后台,按Ctrl+C,停机
<bochs:32> pb 0x08f0     //物理地址断点
<bochs:33> c                  // continue

4、回到minix,随意按一外键

5、Bochs自动进入debug
(0) Breakpoint 4, 0x8f0 in ?? ()
Next at t=2340001600
(0) [0x000008f0] 0030:000000f0 (unk. ctxt): call 0x3c0                ; e8cb0200
00
<bochs:46>

6、开始进入Hwint01的中断陷入部分.

7、查看进入中断例程的寄存器装态
 (0) [0x000008f0] 0030:000000f0 (unk. ctxt): call 0x3c0                ; e8cb0200
 00
 <bochs:16> info r
 eax            0x0              0
 ecx            0x0              0
 edx            0x0              0
 ebx            0x0              0
 esp            0x735c           0x735c
 ebp            0x0              0x0
 esi            0x0              0
 edi            0x0              0
 eip            0xf0             0xf0
 eflags         0x2              2
 cs             0x30             48
 ss             0x18             24
 ds             0xf              15
 es             0xf              15
 fs             0xf              15
 gs             0xf              15
     寄存器表1
 <bochs:17> s
 Next at t=468489601
 (0) [0x00000bc0] 0030:000003c0 (unk. ctxt): cld                       ; fs
 <bochs:18> info r
 eax            0x0              0
 ecx            0x0              0
 edx            0x0              0
 ebx            0x0              0
 esp            0x7358           0x7358
 ebp            0x0              0x0
 esi            0x0              0
 edi            0x0              0
 eip            0x3c0            0x3c0
 eflags         0x2              2
 cs             0x30             48
 ss             0x18             24
 ds             0xf              15
 es             0xf              15
 fs             0xf              15
 gs             0xf              15
     寄存器表2
注解:call指令,使esp自减了=0x735c-0x7358=4(byte)
 <bochs:33> u /40 0xbc0
 00000bc0: (                    ): cld                       ; fc
 00000bc1: (                    ): pushad                    ; 60
 00000bc2: (                    ): opsize push ds            ; 661e
 00000bc4: (                    ): opsize push es            ; 6606
 00000bc6: (                    ): opsize push fs            ; 660fa0
 00000bc9: (                    ): opsize push gs            ; 660fa8
 00000bcc: (                    ): mov dx, ss                ; 8cd2
 00000bce: (                    ): mov ds, dx                ; 8eda
 00000bd0: (                    ): mov es, dx                ; 8ec2
 00000bd2: (                    ): mov eax, esp              ; 89e0
 00000bd4: (                    ): inc byte ptr [ds:0x6c58]  ; fe05586c0000
 00000bda: (                    ): jnz 0xbec                 ; 7510
 00000bdc: (                    ): mov esp, 0x2ff8           ; bcf82f0000
 00000be1: (                    ): push 0x433                ; 6833040000
 00000be6: (                    ): xor ebp, ebp              ; 31ed
 00000be8: (                    ): jmp dword ptr ds:[eax+0x28] ; ff6028
 字节对齐(4的整倍数 .align 4)
 00000bec: (                    ): push 0x455                ; 6855040000
 00000bf1: (                    ): jmp dword ptr ds:[eax+0x28] ; ff6028
注解:pushad指令等价于
Temp ← (ESP);
Push(EAX);
Push(ECX);
Push(EDX);
Push(EBX);
Push(Temp);
Push(EBP);
Push(ESI);
Push(EDI);
把ds,es,fs,gs,入栈。后四个共2*4=8byte,pushad的入栈大小为8*4=32byte。因此total入栈为0x28
(40D),故jmp dword ptr ds:[eax+0x28]的地址为调用call save时所保存的CS:IP.

与源码/kernel/Mpx386.s的save对比
!*===========================================================================*
!*    save          *
!*===========================================================================*
! Save for protected mode.
! This is much simpler than for 8086 mode, because the stack already points
! into the process table, or has already been switched to the kernel stack.

 .align 16
save:
 cld   ! set direction flag to a known value
 pushad   ! save "general" registers
    o16 push ds  ! save ds
    o16 push es  ! save es
    o16 push fs  ! save fs
    o16 push gs  ! save gs
 mov dx, ss  ! ss is kernel data segment
 mov ds, dx  ! load rest of kernel segments
 mov es, dx  ! kernel does not use fs, gs
 mov eax, esp ! prepare to return
 incb (_k_reenter) ! from -1 if not reentering
 jnz set_restart1 ! stack is already kernel stack
 mov esp, k_stktop
 push _restart ! build return address for int handler
 xor ebp, ebp ! for stacktrace
 jmp RETADR-P_STACKBASE(eax)

 .align 4
set_restart1:
 push restart1
 jmp RETADR-P_STACKBASE(eax)

从代码得出,保存恢复工作分两种,一种是有当前是还没有被嵌套的中断,另一种是已经被嵌套的中断。
前者要首先恢复使用kernel栈,然后把_restart入栈待中断完成后做恢复工作且调用/proc.c中的un_hold,
激活被挂起的其它的中断例程。

后者则只把restart1入栈,待中断完成后做恢复工作。

最后两者都利用jmp RETADR-P_STACKBASE(eax),返回save之后的代码。

以下是动态调试结果:
单步执行到0xbd4
(0) [0x00000bd4] 0030:000003d4 (unk. ctxt): inc byte ptr [ds:0x6c58]  ; fe05586c
0000
<bochs:55> x /b 0x18:0x6c58
[bochs]:
0x00017558 <bogus+       0>:    0xff
注解:incb (_k_reenter),_k_reenter记录中断嵌套次数。
如果_k_reenter是-1(0xff为-1的补码,执行inc指令后为0),则当前没有嵌套且栈并非为kernel的栈,
把esp指回kernel栈顶,使用kernel栈
然后把恢复寄存器工作交由_restart的地址入kernel栈,待中断工作完成后,重调用_restart

 mov esp, k_stktop
 push _restart ! build return address for int handler
以下是_restart的程序
!*===========================================================================*
!*    restart          *
!*===========================================================================*
_restart:

! Flush any held-up interrupts.
! This reenables interrupts, so the current interrupt handler may reenter.
! This does not matter, because the current handler is about to exit and no
! other handlers can reenter since flushing is only done when k_reenter == 0.

 cmp (_held_head), 0 ! do fast test to usually avoid function call
 jz over_call_unhold
 call _unhold  ! this is rare so overhead acceptable
over_call_unhold:
 mov esp, (_proc_ptr) ! will assume P_STACKBASE == 0
 lldt P_LDT_SEL(esp)  ! enable segment descriptors for task
 lea eax, P_STACKTOP(esp) ! arrange for next interrupt
 mov (_tss+TSS3_S_SP0), eax ! to save state in process table
restart1:
 decb (_k_reenter)
    o16 pop gs
    o16 pop fs
    o16 pop es
    o16 pop ds
 popad
 add esp, 4  ! skip return adr
 iretd   ! continue process
注解:当k_reenter==0即嵌套重数为0时,调用proc.c的unhold例程,激活被挂起的中断例程。

否则,当前栈为kernel栈,直接跳转至set_restart1,恢复寄存器工作交给restart1的地址push至栈中,待中断完成后,执行。
restart1:
 decb (_k_reenter)
    o16 pop gs
    o16 pop fs
    o16 pop es
    o16 pop ds
 popad
 add esp, 4  ! skip return adr
 iretd   ! continue process

继续动态调试,单步执行到返回
 00000be8: (                    ): jmp dword ptr ds:[eax+0x28] ; ff6028
 <bochs:61> s
 Next at t=468489616
 (0) [0x00000be8] 0030:000003e8 (unk. ctxt): jmp dword ptr ds:[eax+0x28] ; ff6028

 <bochs:62> info r
 eax            0x7330           29488
 ecx            0x0              0
 edx            0x18             24
 ebx            0x0              0
 esp            0x2ff4           0x2ff4
 ebp            0x0              0x0
 esi            0x0              0
 edi            0x0              0
 eip            0x3e8            0x3e8
 eflags         0x42             66
 cs             0x30             48
 ss             0x18             24
 ds             0x18             24
 es             0x18             24
 fs             0xf              15
 gs             0xf              15
 <bochs:63> x 0x18:0x7358
 0x00017c58 <bogus+       0>:    0x000000f5

注解:所以返回的IP为0xf5,近jmp所以CS为0x30,跳回0x30:0xf5(当前为保护模式)
<bochs:84> calc 0x30:0xf5
0x8f5 2293
跳回的物理地址0x8f5
-------------------------------hwint01的内存反汇编结果
<bochs:67> u /25 0x8f0
000008f0: (                    ): call 0xbc0                ; e8cb020000
000008f5: (                    ): in al, 0x21               ; e421
000008f7: (                    ): or al, 0x2                ; 0c02
000008f9: (                    ): out 0x21, al              ; e621
000008fb: (                    ): mov al, 0x20              ; b020
000008fd: (                    ): out 0x20, al              ; e620
000008ff: (                    ): sti                       ; fb
00000900: (                    ): push 0x1                  ; 6a01
00000902: (                    ): call dword ptr [ds:0x6cc8] ; ff15c86c0000
//调用keyboard的中断例程
00000908: (                    ): pop ecx                   ; 59
00000909: (                    ): cli                       ; fa
0000090a: (                    ): test eax, eax             ; 85c0
0000090c: (                    ): jz 0x914                  ; 7406
0000090e: (                    ): in al, 0x21               ; e421
00000910: (                    ): and al, 0xfd              ; 24fd
00000912: (                    ): out 0x21, al              ; e621
00000914: (                    ): retn                      ; c3
---------------------------------------------------------------------------------

 

 

 

 




(待扩展)

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值