Welcome to My World (www.dgmayor.com)

소프트웨어/C & 자료구조 & 커널 & DB

12. 인터럽트 IDT 구현

dgmayor 2022. 6. 14. 11:20
728x90
 

10 ~ 13강 소스코드입니다.
 

--------------------------------------------------------------------------------
 

저번에 설명했듯이 인터럽트를 구현하기 위해서는 크게 3단계를 구현해야 합니다.
 
1. PIC 셋팅 ( 하드웨어 셋팅 )
2. IDT ( 전에 구현했던 GDT와 같은 종류 )
3. ISR ( 실제 인터럽트 발생 시 실행되는 코드 )
 
요번 포스트에서는 저번에 IDT 선언에 이어 실제로 구현해보도록 하겠습니다.
 
당장 interrupt.c를 만들어 다음과 같이 작성해봅시다.
 
 
1
2
3
4
5
6
7
8
9
10
11
#include "interrupt.h"
#include "function.h"
 
struct IDT inttable[3];
struct IDTR idtr = { 256 * 8 - 1,0 };
 
void init_intdesc() {}
void idt_ignore() {}
void idt_timer() {}
void idt_keyboard() {}
 
cs
 

먼저 inttable을 봅시다. 총 3개를 선언했습니다. 이는 각각 ignore, timer, keyboard 에 대한 IDT를 저장하기 위함입니다. 이 저장은 init_intdesc() 에서 구현할 것입니다.
 
idtr을 봅시다. 256개를 만들겠다 ( 그런데 한 개에 8바이트, 즉 64비트짜리이니 8을 곱한 겁니다 이는 GDT 생성때와 완전히 같은 논리입니다.) 그리고 적재될 주소는 0x0 번지다 를 그대로 짠 겁니다.
 
자 그럼 init_intdest()를 본격적으로 짜봅시다.
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
void init_intdesc()
{
 
    int i,j;
    unsigned int ptr;
    unsigned short *isr;
 
    {  // 0x00 : isr_ignore
        ptr = (unsigned int)idt_ignore;
        inttable[0].selector = (unsigned short)0x08;
        inttable[0].type = (unsigned short)0x8E00;
        inttable[0].offsetl = (unsigned short)(ptr & 0xFFFF);
        inttable[0].offseth = (unsigned short)(ptr >> 16);
 
    }
 
    {  // 0x01 : isr_timer
        ptr = (unsigned int)idt_timer;
        inttable[1].selector = (unsigned short)0x08;
        inttable[1].type = (unsigned short)0x8E00;
        inttable[1].offsetl = (unsigned short)(ptr & 0xFFFF);
        inttable[1].offseth = (unsigned short)(ptr >> 16);
 
    }
 
    {  // 0x02 : isr_keyboard
        ptr = (unsigned int)idt_keyboard;
        inttable[2].selector = (unsigned short)0x08;
        inttable[2].type = (unsigned short)0x8E00;
        inttable[2].offsetl = (unsigned short)(ptr & 0xFFFF);
        inttable[2].offseth = (unsigned short)(ptr >> 16);
 
    }
}
 
cs
 

상당히 위력적인 코드입니다. 하지만 하나씩 뜯어보면 별 거 없습니다. 그냥 각 IDT 테이블의 자료형에 맞는 데이터를 입력하는 것을 3번 반복한 겁니다. ( 각각 ignore, timer, keyboard )
 
먼저 selector, 즉 우리가 예전에 셋팅했던 보호모드의 selector를 의미합니다. 0x08이 CodeSegment 였으니 이걸 일괄적으로 다 집어넣어줍니다.
 
type은 0x8E00을 집어넣어주면 됩니다. 자세한 것은 지난 포스트를 참고하세요.
 
이제 오프셋을 저장합시다! 이 오프셋은 설명했듯이 각 테이블이 가리키는 함수의 주소가 저장되어야 합니다. 문제는 주소는 32비트인데 IDT 테이블은 이를 16비트로 각각 쪼개서 저장한다는 점입니다. 이를 쪼개고자 먼저 ptr이라고 하는 변수를 선언하고 여기에 IDT에 저장하고자 하는 함수주소를 저장해놓습니다. 그리고 &와 >> 비트 연산을 통해 각각 0~15비트, 16~31비트 부분을 추출해서 저장합니다.
 
논리는 이게 다입니다. 이제 3개의 테이블에 IDT를 저장해두었습니다. 그런데... 전 포스트에서 언급했듯이 이 테이블은 원래 물리주소 0x0번지에 배치되야 하는 친구들입니다. 따라서 해당 정보들을 0x0번지에 복사하는 코드가 필요합니다!
 
다음과 같이 이어서 작성을 해봅시다.
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
// 이어서 계속 작성
 
// 물리주소 0x0 번지에 ISR 배치 시작
 
    for (i = 0; i < 256; i++)
    {
        isr = (unsigned short*)(0x0 + i * 8);
        *isr = inttable[0].offsetl;
        *(isr + 1= inttable[0].selector;
        *(isr + 2= inttable[0].type;
        *(isr + 3= inttable[0].offseth;
 
    }
 
    // 타이머 ISR 등록
    {
        isr = (unsigned short*)(0x0 + 8 * 0x20);
        *isr = inttable[1].offsetl;
        *(isr + 1= inttable[1].selector;
        *(isr + 2= inttable[1].type;
        *(isr + 3= inttable[1].offseth;
    }
 
    // 키보드 ISR 등록
 
    {
        isr = (unsigned short*)(0x0 + 8 * 0x21);
        *isr = inttable[2].offsetl;
        *(isr + 1= inttable[2].selector;
        *(isr + 2= inttable[2].type;
        *(isr + 3= inttable[2].offseth;
    
    }
 
cs
 

자세히보면 별 거 없습니다. 먼저 256번 루프를 돌려서 0x0번지에다가 256개의 IDT 테이블을 다 도배해버립시다. 이렇게 되면 어떤 인터럽트가 발생해도 무조건 idt_ignore 함수를 실행하게 될 것입니다.
 
하지만 우린 타이머와 키보드를 따로 구현하고 싶습니다. 그렇기 때문에 256번 도배를 하고 난 후에 따로 타이머와 키보드를 등록해두는 것입니다. 타이머는 0x20 * 8, 키보드는 0x21* 8 번지에 각각 구현하면 됩니다. (번지가 절대 틀려서는 안됩니다! 이는 PIC에 구현했던 스펙을 그대로 따르기 때문입니다. )
 
좋습니다. 이제 모든 것이 완벽하게 끝났습니다. 이제 남은 것은 CPU에게 " 이제 IDT 세팅 다 끝났어. " 라고 알려주는 코드를 짜는 겁니다. 마지막으로 다음과 같이 작성해봅시다.
 
1
2
3
4
5
6
7
8
9
//  인터럽트 작동 시작
 
    __asm__ __volatile__("mov eax, %0"::"r"(&idtr));
    __asm__ __volatile__("lidt [eax]");
    __asm__ __volatile__("mov al,0xFC");
    __asm__ __volatile__("out 0x21,al");
    __asm__ __volatile__("sti");
 
    return;
cs
 
 

C언어 내부에서도 어셈블리를 다음과 같이 쓸 수가 있습니다! ( 구체적인 용법을 알고 싶다면 다음 링크를 참고하세요 : https://www.codeproject.com/Articles/15971/Using-Inline-Assembly-in-C-C )
 
하지만 이렇게만 써서는 gcc 컴파일러가 알아먹지 못합니다! 왜냐면 지금까지 써온 어셈블리의 문법이 intel 문법인데 gcc의 기본 셋팅은 AT&T 문법을 따르기 때문입니다.
 
따라서 intel 문법을 따르면서 gcc로 컴파일하고 싶다면 다음과 같이 컴파일 옵션을 넣어줘야 합니다. -masm=intel
 
사실을 알았으니 makefile 에 들어가서 해당 옵션을 모두 넣어줍시다.
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
CC = gcc
 
final.img : Boot.img Sector2.img disk.img
    cat Boot.img Sector2.img disk.img > final.img
 
disk.img : main.img
    objcopy -O binary main.img disk.img
 
main.img :main.o function.o interrupt.o
    ld -melf_i386 -Ttext 0x10200 -nostdlib main.o function.o interrupt.o -o main.img
 
main.o : main.c
    gcc -c  -masm=intel -m32 -ffreestanding main.c -o main.o
 
function.o : function.c
    gcc --masm=intel -m32 -ffreestanding function.c -function.o
 
interrupt.o : interrupt.c
    gcc --masm=intel -m32 -ffreestanding interrupt.c -o interrupt.o
 
Boot.img : Boot.asm
    nasm -f bin -o Boot.img Boot.asm
 
Sector2.img : Sector2.asm
    nasm -f bin -o Sector2.img Sector2.asm
 
clean :
    rm *.o *.img
cs
 
 

각 -masm=intel 옵션을 목적파일(.o) 생성하는 코드에 넣어주고, interrupt.o 를 생성하는 코드도 한 줄 추가합시다.
 
 
 
--------------------------------------------------------------------------------
 
 

좋습니다! 이제 init_intdesc() 를 모두 짰고, makefile도 알맞게 수정했습니다.
 
지금까지 짠 interrupt.c 의 코드는 다음과 같습니다.
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
#include "interrupt.h"
#include "function.h"
 
struct IDT inttable[3];
struct IDTR idtr = { 256 * 8 - 1,0 };
 
void init_intdesc()
{
 
    int i,j;
    unsigned int ptr;
    unsigned short *isr;
 
    {  // 0x00 : isr_ignore
        ptr = (unsigned int)idt_ignore;
        inttable[0].selector = (unsigned short)0x08;
        inttable[0].type = (unsigned short)0x8E00;
        inttable[0].offsetl = (unsigned short)(ptr & 0xFFFF);
        inttable[0].offseth = (unsigned short)(ptr >> 16);
 
    }
 
    {  // 0x01 : isr_timer
        ptr = (unsigned int)idt_timer;
        inttable[1].selector = (unsigned short)0x08;
        inttable[1].type = (unsigned short)0x8E00;
        inttable[1].offsetl = (unsigned short)(ptr & 0xFFFF);
        inttable[1].offseth = (unsigned short)(ptr >> 16);
 
    }
 
    {  // 0x02 : isr_keyboard
        ptr = (unsigned int)idt_keyboard;
        inttable[2].selector = (unsigned short)0x08;
        inttable[2].type = (unsigned short)0x8E00;
        inttable[2].offsetl = (unsigned short)(ptr & 0xFFFF);
        inttable[2].offseth = (unsigned short)(ptr >> 16);
 
    }
 
    // 물리주소 0x0 번지에 ISR 배치 시작
 
    for (i = 0; i < 256; i++)
    {
        isr = (unsigned short*)(0x0 + i * 8);
        *isr = inttable[0].offsetl;
        *(isr + 1= inttable[0].selector;
        *(isr + 2= inttable[0].type;
        *(isr + 3= inttable[0].offseth;
 
    }
 
    // 타이머 ISR 등록
    {
        isr = (unsigned short*)(0x0 + 8 * 0x20);
        *isr = inttable[1].offsetl;
        *(isr + 1= inttable[1].selector;
        *(isr + 2= inttable[1].type;
        *(isr + 3= inttable[1].offseth;
    }
 
    // 키보드 ISR 등록
 
    {
        isr = (unsigned short*)(0x0 + 8 * 0x21);
        *isr = inttable[2].offsetl;
        *(isr + 1= inttable[2].selector;
        *(isr + 2= inttable[2].type;
        *(isr + 3= inttable[2].offseth;
    
    }
 
    //  인터럽트 작동 시작
 
    __asm__ __volatile__("mov eax, %0"::"r"(&idtr));
    __asm__ __volatile__("lidt [eax]");
    __asm__ __volatile__("mov al,0xFC");
    __asm__ __volatile__("out 0x21,al");
    __asm__ __volatile__("sti");
 
    return;
 
 
}
 
void idt_ignore() {}
void idt_timer() {}
void idt_keyboard() {}
 
cs
 
 

멋지게 짰긴 짰는데... 정작 인터럽트가 발생하면 실행되어야 할 함수들이 맨 밑줄에 구현이 하나도 안되어있군요!
 
다음 강의에서는 이 함수들을 구현해보도록 하겠습니다.
 
감사합니다.
728x90