关于Linux0.00中的时钟中断代码的一点理解

在读Linux0.00 head.s的代码时,时钟中断这段代码折腾了我半天才弄懂,先贴上代码

 

  1 /*
  2  *  head.s contains the 32-bit startup code.
  3  *  Two L3 task multitasking. The code of tasks are in kernel area, 
  4  *  just like the Linux. The kernel code is located at 0x10000. 
  5  */
  6 KRN_BASE     = 0x10000
  7 TSS0_SEL    = 0x20
  8 LDT0_SEL    = 0x28
  9 TSS1_SEL    = 0X30
 10 LDT1_SEL    = 0x38
 11 
 12 .text
 13 startup_32:
 14     movl $0x10,%eax
 15     mov %ax,%ds
 16     mov %ax,%es
 17     mov %ax,%fs
 18     mov %ax,%gs
 19     lss stack_ptr,%esp
 20 
 21 # setup base fields of descriptors.
 22     movl $KRN_BASE, %ebx
 23     movl $gdt, %ecx
 24     lea tss0, %eax
 25     movl $TSS0_SEL, %edi    
 26     call set_base
 27     lea ldt0, %eax
 28     movl $LDT0_SEL, %edi
 29     call set_base
 30     lea tss1, %eax
 31     movl $TSS1_SEL, %edi 
 32     call set_base
 33     lea ldt1, %eax
 34     movl $LDT1_SEL, %edi
 35     call set_base
 36 
 37     call setup_idt
 38     call setup_gdt
 39     movl $0x10,%eax        # reload all the segment registers
 40     mov %ax,%ds        # after changing gdt. 
 41     mov %ax,%es
 42     mov %ax,%fs
 43     mov %ax,%gs
 44     lss stack_ptr,%esp
 45 
 46 # setup up timer 8253 chip.
 47     movb $0x36, %al
 48     movl $0x43, %edx
 49     outb %al, %dx
 50     movl $11930, %eax        # timer frequency 100 HZ 
 51     movl $0x40, %edx
 52     outb %al, %dx
 53     movb %ah, %al
 54     outb %al, %dx
 55 
 56 # setup timer & system call interrupt descriptors.
 57     movl $0x00080000, %eax    
 58     movw $timer_interrupt, %ax
 59     movw $0x8E00, %dx
 60     movl $0x20, %ecx
 61     lea idt(,%ecx,8), %esi
 62     movl %eax,(%esi) 
 63     movl %edx,4(%esi)
 64     movw $system_interrupt, %ax
 65     movw $0xef00, %dx
 66     movl $0x80, %ecx
 67     lea idt(,%ecx,8), %esi
 68     movl %eax,(%esi) 
 69     movl %edx,4(%esi)
 70 
 71 # unmask the timer interrupt.
 72     movl $0x21, %edx
 73     inb %dx, %al
 74     andb $0xfe, %al
 75     outb %al, %dx
 76 
 77 # Move to user mode (task 0)
 78     pushfl
 79     andl $0xffffbfff, (%esp)
 80     popfl
 81     movl $TSS0_SEL, %eax
 82     ltr %ax
 83     movl $LDT0_SEL, %eax
 84     lldt %ax 
 85     movl $0, current
 86     sti
 87     pushl $0x17
 88     pushl $stack0_ptr
 89     pushfl            /* 手动设置返回环境*/
 90     pushl $0x0f         /**/
 91     pushl $task0         /**/
 92     iret
 93 
 94 /****************************************/
 95 setup_gdt:
 96     lgdt lgdt_opcode
 97     ret
 98 
 99 setup_idt:
100     lea ignore_int,%edx
101     movl $0x00080000,%eax
102     movw %dx,%ax        /* selector = 0x0008 = cs */
103     movw $0x8E00,%dx    /* interrupt gate - dpl=0, present */
104     lea idt,%edi
105     mov $256,%ecx
106 rp_sidt:
107     movl %eax,(%edi)
108     movl %edx,4(%edi)
109     addl $8,%edi
110     dec %ecx
111     jne rp_sidt
112     lidt lidt_opcode
113     ret
114 
115 # in: %eax - logic addr; %ebx = base addr ; 
116 # %ecx - table addr; %edi - descriptors offset.
117 set_base:
118     addl %ebx, %eax
119     addl %ecx, %edi
120     movw %ax, 2(%edi)
121     rorl $16, %eax
122     movb %al, 4(%edi)
123     movb %ah, 7(%edi)
124     rorl $16, %eax
125     ret
126 
127 write_char:
128     push %gs
129     pushl %ebx
130     pushl %eax
131     mov $0x18, %ebx
132     mov %bx, %gs
133     movl scr_loc, %bx
134     shl $1, %ebx
135     movb %al, %gs:(%ebx)
136     shr $1, %ebx
137     incl %ebx
138     cmpl $2000, %ebx
139     jb 1f
140     movl $0, %ebx
141 1:    movl %ebx, scr_loc    
142     popl %eax
143     popl %ebx
144     pop %gs
145     ret
146 
147 /***********************************************/
148 /* This is the default interrupt "handler" :-) */
149 .align 2
150 ignore_int:
151     push %ds
152     pushl %eax
153     movl $0x10, %eax
154     mov %ax, %ds
155     movl $67, %eax            /* print 'C' */
156     call write_char
157     popl %eax
158     pop %ds
159     iret
160 
161 /* Timer interrupt handler */ 
162 .align 2
163 timer_interrupt:
164     push %ds
165     pushl %edx
166     pushl %ecx
167     pushl %ebx
168     pushl %eax
169     movl $0x10, %eax
170     mov %ax, %ds
171     movb $0x20, %al
172     outb %al, $0x20
173     movl $1, %eax
174     cmpl %eax, current
175     je 1f
176     movl %eax, current
177     ljmp $TSS1_SEL, $0
178     jmp 2f
179 1:    movl $0, current
180     ljmp $TSS0_SEL, $0
181 2:    popl %eax
182     popl %ebx
183     popl %ecx
184     popl %edx
185     pop %ds
186     iret
187 
188 /* system call handler */
189 .align 2
190 system_interrupt:
191     push %ds
192     pushl %edx
193     pushl %ecx
194     pushl %ebx
195     pushl %eax
196     movl $0x10, %edx
197     mov %dx, %ds
198     call write_char
199     popl %eax
200     popl %ebx
201     popl %ecx
202     popl %edx
203     pop %ds
204     iret
205 
206 /*********************************************/
207 current:.long 0
208 scr_loc:.long 0
209 
210 .align 2
211 .word 0
212 lidt_opcode:
213     .word 256*8-1        # idt contains 256 entries
214     .long idt + KRN_BASE    # This will be rewrite by code. 
215 .align 2
216 .word 0
217 lgdt_opcode:
218     .word (end_gdt-gdt)-1    # so does gdt 
219     .long gdt + KRN_BASE    # This will be rewrite by code.
220 
221     .align 3
222 idt:    .fill 256,8,0        # idt is uninitialized
223 
224 gdt:    .quad 0x0000000000000000    /* NULL descriptor */
225     .quad 0x00c09a01000007ff    /* 8Mb 0x08, base = 0x10000 */
226     .quad 0x00c09201000007ff    /* 8Mb 0x10 */
227     .quad 0x00c0920b80000002    /* screen 0x18 - for display */
228 
229     .quad 0x0000e90100000068    # TSS0 descr 0x20
230     .quad 0x0000e20100000040    # LDT0 descr 0x28
231     .quad 0x0000e90100000068    # TSS1 descr 0x30
232     .quad 0x0000e20100000040    # LDT1 descr 0x38
233 end_gdt:
234     .fill 128,4,0
235 stack_ptr:
236     .long stack_ptr
237     .word 0x10
238 
239 /*************************************/
240 .align 3
241 ldt0:    .quad 0x0000000000000000
242     .quad 0x00c0fa01000003ff    # 0x0f, base = 0x10000
243     .quad 0x00c0f201000003ff    # 0x17
244 tss0:
245     .long 0             /* back link */
246     .long stack0_krn_ptr, 0x10    /* esp0, ss0 */
247     .long 0, 0            /* esp1, ss1 */
248     .long 0, 0            /* esp2, ss2 */
249     .long 0                /* cr3 */
250     .long 0            /* eip */
251     .long 0            /* eflags */
252     .long 0, 0, 0, 0        /* eax, ecx, edx, ebx */
253     .long 0, 0, 0, 0    /* esp, ebp, esi, edi */
254     .long 0,0,0,0,0,0 /* es, cs, ss, ds, fs, gs */
255     .long LDT0_SEL            /* ldt */
256     .long 0x8000000            /* trace bitmap */
257 
258     .fill 128,4,0
259 stack0_krn_ptr:
260     .long 0
261 
262 /************************************/
263 .align 3
264 ldt1:    .quad 0x0000000000000000
265     .quad 0x00c0fa01000003ff    # 0x0f, base = 0x10000
266     .quad 0x00c0f201000003ff    # 0x17
267 tss1:
268     .long 0             /* back link */
269     .long stack1_krn_ptr, 0x10    /* esp0, ss0 */
270     .long 0, 0            /* esp1, ss1 */
271     .long 0, 0            /* esp2, ss2 */
272     .long 0                /* cr3 */
273     .long task1            /* eip */
274     .long 0x200            /* eflags */
275     .long 0, 0, 0, 0        /* eax, ecx, edx, ebx */
276     .long stack1_ptr, 0, 0, 0    /* esp, ebp, esi, edi */
277     .long 0x17,0x0f,0x17,0x17,0x17,0x17 /* es, cs, ss, ds, fs, gs */
278     .long LDT1_SEL            /* ldt */
279     .long 0x8000000            /* trace bitmap */
280 
281     .fill 128,4,0
282 stack1_krn_ptr:
283     .long 0
284 
285 /************************************/
286 task0:
287     movl $0x17, %eax
288     movw %ax, %ds
289     movl $65, %al              /* print 'A' */
290     int $0x80
291     movl $0xfff, %ecx
292 1:    loop 1b
293     jmp task0 
294 
295     .fill 128,4,0
296 stack0_ptr:
297     .long 0
298 
299 task1:
300     movl $0x17, %eax
301     movw %ax, %ds
302     movl $66, %al              /* print 'B' */
303     int $0x80
304     movl $0xfff, %ecx
305 1:    loop 1b
306     jmp task1
307 
308     .fill 128,4,0 
309 stack1_ptr:
310     .long 0
311 
312 /*** end ***/

 

 

 question:

在代码的第177行(177 ljmp $TSS1_SEL, $0)和第180行(180 ljmp $TSS0_SEL, $0)可以看到,中断程序转移到对应的任务去执行了,后边的代码怎么执行?iret永远也执行不到了么?

answer:

实际上,程序在开始运行时,从第79行(79 andl $0xffffbfff, (%esp))可以看到标志寄存器的中断嵌套位NT被设置成了0,也就是说中断返回时,将会使用堆栈保存的任务状态,而不是任务切换的方式保存。81,82行设置了当前任务状态寄存器为TSS0_SEL,可以看到tss0里面,除了esp0,ss0,ldt,trace bitmap设置了外,其余是0.
嵌套任务标志NT(Nested Task) 
嵌套任务标志NT用来控制中断返回指令IRET的执行。具体规定如下:
(1)、当NT=0,用堆栈中保存的值恢复EFLAGS、CS和EIP,执行常规的中断返回操作; (2)、当NT=1,通过任务转换实现中断返回。

随后在手动设置返回环境后,进入task0,在第一次时钟中断产生时,进入时钟中断处理程序,此时task0的状态全都保存到了堆栈,然后经判断转去task1,在这里完成了复杂的工作,这个ljmp意义很大(177,180),

第一次时钟中断

ljmp(177):

  cpu首先把当前状态(注意是当前状态,而不是task0的状态)保存到tss0,此时tss0的eip指向下一条语句(178 jmp 2f)。task0的运行状态被压入堆栈,此时的tr为TSS0_SEL

  然后cpu转去执行tss1_sel 描叙的内容,可以看到tss1里面存放的是完整的信息,并且eip指向task1。并且重新加载tr为TSS1_SEL

第二次时钟中断,

ljmp(180)

  同样cpu首先把当前状态(注意是当前状态,而不是task1的状态)保存到tss1,此时tss1的eip指向下一条语句(181 2: popl %eax),这条语句就是上一次中断要跳转的位置。task1的运行状态被压入堆栈,此时的tr为TSS1_SEL

  然后cpu转去执行tss0_sel描叙的内容,注意tss0_sel描述符里面存放的是上次时钟中断的位置,所以会立即执行178 jmp 2f,最后执行iret,还记得上次堆栈里面放的是task0的运行状态,iret会恢复堆栈内容到寄存器(根据NT标志),因此这次中断后会执行task0中断产生时的下一条语句。同事cpu重新加载tr为tss_se0

第三次。。。。过后就如第二样了,就第一次有点差别,第一次切换时,是直接去执行task1,以后的task0,task1,都从堆栈中恢复。

写的有点乱,自己记录一下,总算是明白这个时钟中断处理程序是怎么切换任务的了。

参考:Linux内核完全剖析 基于0.12内核

小弟初入道,高手路过请指教!!!

posted on 2013-07-26 13:00  ssuupper  阅读(566)  评论(0)    收藏  举报