1. IDT 만들기
IDT는 Interrupt Descriptor Table의 약자로, 인터럽트를 구현하기 위한 테이블입니다.
메모리 중 어느 곳이든 저장이 가능하며, GDT 디스크립터와 유사한 모양의 256개의 디스크립터로 구성됩니다.
다음 그림은 IDT의 구조입니다.
15 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
0 |
(1)핸들러의 오프셋 15 – 0 |
|||||||||||||||
(2)핸들러의 코드 세그먼트 셀렉터 |
|||||||||||||||
(3)P |
(4) DPL |
(5) 0 |
(6)D |
(7)1 |
(8) 1 |
(9)0 |
사용안함 |
||||||||
(10) 핸들러의 오프셋 31 – 16 |
|||||||||||||||
63 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
48 |
(1)핸들러의 오프셋 15 – 0
처음 16비트 한 워드에는 인터럽트 핸들러가 자리하고 있는 RAM 상의 물리 주소의 0 ~ 15비트(오프셋)을 기입
(2)핸들러의 코드 세그먼트 셀렉터
인터럽트 핸들러가 자리하고 있는 코드 세그먼트 셀렉터의 값을 기입합니다.
인터럽트 핸들러는 항상 Protected 모드에서 동작하므로 커널 모드 코드 세그먼트 셀렉터의 값을 기입하면 됩니다.
(3)P bit
GDT 디스크립터와 마찬가지로 커널 모드의 코드 세그먼트가 RAM 상에 존재하는지 나타냄. 항상 1로 세트
(4)DPL
핸들러가 실행될 특권 레벨을 지정하는 플래그 입니다. 인터럽트 핸들러는 항상 커널 모드에서 동작하기 때문에 0 ~ 3 중 0 레벨을 지정 하여야 함으로 이진수로 00 기입합니다.
(5)0
이 디스크립터가 IDT에 위치한 인터럽트 관련 디스크립터라는 것을 CPU에게 알리기 위해 그대로 0 기입합니다.
(6)D
현재지정한 코드 세그먼트가 16비트인지 32 비트인지를 나타냅니다.
0이면 16비트를 나타내고 1이면 32비트를 나타내기 때문에 프로텍트 모드에서 동작되는 인터럽트 핸들러는 1로 세트 합니다.
(10) 핸들러의 오프셋 3 1 – 16
오프셋의 상위 16비트를 기입합니다.
강제사항은 아니지만 사용하고 있는PC의 메인보드의 인터럽트 관련 하드웨어가 256개의 인터럽트를 받아들이도록 디자인 되어 있기 때문에 여기서는 IDT를 메모리상에 256개를 만듭니다.
2. RAM 상에 IDT를 만들기
cld mov ax, SysDataSelector mov es, ax xor eax, eax xor ecx, ecx mov ax, 256 ; ax 레지스터를 몇 번 복사 할 것인지 알려주는 카운터로 사용 mov edi, 0
loop_idt: ; 루프를 돌면서 idt를 복사 lea esi, [idt_ignore] ; idt_ignore은 idt 샘플 mov cx. 8 ; idt가 8byte 이다. rep movsb ; ds:esi의 내용을 cx의 값(단위는 바이트) 만큼 es:edi 위치에 복사 dec ax jnz loop_idt |
Rep movsb
Rep – 스트링 명령어를 반복
Movsb – move string byte 한 바이트 씩 si 레지스터에 저장된 위치의 데이터를 di 레지스터에 저장된 위치로 복사, ES:DI, DS:SI가 디폴트
Idt_ignore
idt_ignore: dw isr_ignore ; 실행되어야 할 핸들러 루틴 dw SysCodeSelector db 0 db 0x8E dw 0x0001 |
샘플로 작성된 IDT의 내용이 디스크립터의 모양과 같음을 알 수 있습니다.
15 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
0 |
Isr_ignore |
|||||||||||||||
SysCodeSelector |
|||||||||||||||
1 |
00 |
0 |
1 |
1 |
1 |
0 |
0 |
||||||||
0x0001(커널이 0x10000 번지에 적재되기 때문에 0x000010000가 더해저야 함) |
|||||||||||||||
63 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
48 |
15 | 0 | ||||||||||||||
인터럽트 서브루틴의 예시
isr_ignore: push gs ; CPU의 레지스터 값과 FLAG를 스택에 보존 push fs ; push esi ; push ds ; pushad ; pushfd ;
mov ax, VideoSelector ;인터럽트 처리 mov es, ax ; mov edi, (80*7*2) ; lea esi, [msg_isr_ignore]; call printf
popfd ; CPU의 레지스터 값을 복원 popad ; pop ds ; pop ds ; pop fs ; pop gs ;
iret ; 인터럽트 루틴을 마치고 돌아감 |
3. IDTR
CPU가 인터럽트에 걸렸을 때 IDT를 참조할 수 있도록 IDTR 레지스터에 등록해야 합니다.
Lidt 명령어에 해당 내용을 담고 있는 주소를 operand로 주어 등록할 수 있습니다.
lidt [idtr]
idtr: dw 256*8-1;(size 이므로 -1 해줘야 함) dd 0 |
Idtr 레지스터구조는 다음과 같습니다.
IDT Limit – dw IDT의 크기 (16비트)
IDT Base Address – dd IDT의 시작점 (32 비트)
인터럽트가 걸렸을 때 핸들러가 호출되기까지의 과정은 다음과 같습니다.
0x20번의 인터럽트가 발생
해당 인터럽트 디스크립터의 위치를 찾는다
1)CPU가 IDTR 참조
2)IDTR의 IDT의 Base Address를 참조하여 IDT의 첫번째 번지 부분으로 이동
3)IDT의 첫 번째 번지에서 0x20번째의 디스크립터를 찾아냄
해당 인터럽트 디스크립터의 물리 주소를 찾는다.(세그먼트의 base address를 구해서 + offset)
1)디스크립터의 세그먼트 셀렉터 값을 가지고 GDT에 해당하는 디스크립터를 찾아냄
2)GDT의 디스크립터를 갖고 RAM 상의 해당 세그먼트의 Base Address를 찾아냄
3)IDT의 0x20번째 디스크립터에 포함된 오프셋 값을 가지고 인터럽트 핸들러 루틴이 세그먼트 범위 안에서 실제로 위치한 것을 찾아냄
인터럽트 루틴을 수행하고 인터럽트를 마친다.
Iret 명령이 내려지면 처음 인터럽트가 걸렸던 프로그램의 인터럽트가 걸렸던 명령문 바로 다음 명령으로 돌아감.
4. 소프트웨어 인터럽트 걸기
sti ; Set Interrupts int 0x77 ; 0x77번 인터럽트를 건다 jmp $ ; while(1){}; |
인터럽트 관련 명령어
- CLI(Clear Interrupts)
- STI(SeT Interrupts)
- POPF(Pop Flags)
sti명령으로 인터럽트를 활성화 시킵니다. 이 명령어를 실행하면
EFLAGS의IF 비트가 세트 되어 이후 명령부터 인터럽트를 받아들일 수 있게 됩니다.
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
I D |
V I P |
V I F |
A C |
V M |
R F |
0 |
N T |
I O P L |
O F |
D F |
I F = 1 |
T F |
S F |
Z F |
0 |
A F |
0 |
P F |
1 |
C F |
EFLAG 레지스터 - 9번 비트는 Interrupt Enable Flag(IF)로 system Flag 중 하나
'Operating Systems > OS 커널 제작' 카테고리의 다른 글
4. Interrupt 와 Exception (3) – 인터럽트 핸들러 (0) | 2015.01.19 |
---|---|
4. Interrupt 와 Exception (2) - PIC (0) | 2015.01.18 |
3. Protected Mode (3) (0) | 2015.01.17 |
3. Protected Mode (2) (0) | 2015.01.15 |
3. Protected Mode (1) (0) | 2015.01.14 |