startup_32() 함수에서는 다음과 같이
setup_idt() 함수를
호출한다 :
--------------------------------------
/usr/src/linux/arch/i386/kernel/head.S
--------------------------------------
startup_32:
:
:
/*
* start system 32-bit setup. We need to re-do some of the things done
* in 16-bit mode for the "real" operations.
*/
call setup_idt
:
: |
그럼 이제 setup_idt() 함수를 보도록 하자.
이 함수에서는 모든 IDT 의 엔트리가 ignore_int()
으로 점프하도록 세팅한다. 실제 쓰이는 인터럽트 디스크립터 테이블의
세팅은 뒤에 paging_init() 함수가 호출 되고서
페이징이 활성화 된 후에 수행한다. setup_idt() 의
코드는 /usr/src/linux/arch/i386/kernel/head.S
에서 찾아볼 수 있다 :
--------------------------------------
/usr/src/linux/arch/i386/kernel/head.S
--------------------------------------
setup_idt:
lea ignore_int,%edx
movl $(__KERNEL_CS << 16),%eax
movw %dx,%ax /* selector = 0x0010 = cs */
movw $0x8E00,%dx /* interrupt gate - dpl=0, present */
lea SYMBOL_NAME(idt_table),%edi
mov $256,%ecx
rp_sidt:
movl %eax,(%edi)
movl %edx,4(%edi)
addl $8,%edi
dec %ecx
jne rp_sidt
ret |
위의 것과 똑같은 어셈블리 코드가 뒤의 섹션에서 설명할 _set_gate()
함수에 나온다. 코드의 설명은 그때 하도록 하고, 그때 설명하지 않을
루프를 도는 부분을 잠시 설명하겠다.
코드에서,
lea SYMBOL_NAME(idt_table),%edi |
부분에서, idt_table 이라는 변수가 저장된 실주소(effective address)를
edi 레지스터에 load 했다. 인텔에서 edi 레지스터는 통상적으로
블록 move 등을 할 때 destination index 로 쓰이는 레지스터이다. 그리고,
통상적으로 카운터로 쓰이는 ecx 레지스터에 idt_table 의 엔트리의 갯수인
256 을 넣고는
rp_sidt: 루프로 넘어가서
이 루프를 256번 반복하면서, 인텔의 인터럽트 게이트의 사이즈(8바이트)만큼
edi 레지스터를 증가시키면서 edi 가 가리키는 곳에 이미 만들어진 인터럽트
게이트를 세팅해 준다. 이 인터럽트 게이트는 모두다 동일하게 ignore_int
함수를 가리키는 것으로써, ignore_int 함수는 그냥 "Unknown interrupt" 를
화면에 출력하고 리턴하는 함수이다. 다음과 같이 정의되어 있다 :
--------------------------------------
/usr/src/linux/arch/i386/kernel/head.S
--------------------------------------
int_msg:
.asciz "Unknown interrupt\n"
ALIGN
ignore_int:
cld
pushl %eax
pushl %ecx
pushl %edx
pushl %es
pushl %ds
movl $(__KERNEL_DS),%eax
movl %eax,%ds
movl %eax,%es
pushl $int_msg
call SYMBOL_NAME(printk)
popl %eax
popl %ds
popl %es
popl %edx
popl %ecx
popl %eax
iret |