11. PIC 컨트롤러와 인터럽트 핸들러를 이용해 인터럽트를 처리하자

들어가기

PIC 컨트롤러로 인터럽트를 프로세서에 전달하고 실행 중이던 코드로의 복귀를 구현해보자.

본론

11.1 PIC 컨트롤러 소개

11.1.1 PIC 컨트롤러란

  • PIC 컨트롤러는 인터럽트 처리에 관련된 세부기능을 프로그래밍할 수 있는 컨트롤러.
  • 마스터-슬레이브 방식으로 연결해 15개의 인터럽트를 처리.
  • PIC 컨트롤러는 각각 8개의 핀으로 총 16개를 구성하지만 핀 하나는 슬레이브와 연결.

11.1.2 PIC 컨트롤러의 구조와 동작 방식

  • PIC 컨트롤러는 내부에 8비트 크기의 IRR, ISR, IMR 레지스터가 존재.
    • IRR, 인터럽트가 발생한 핀의 정보를 관리
    • ISR, 현재 인터럽트 핸들러가 실행 중인 인터럽트 정보를 저장.
    • IMR, 비트가 1로 설정된 인터럽트 핀에서 발생한 요청을 무시하는 역할.

11.2 PIC 컨트롤러 제어

11.2.1 PIC 컨트롤러 초기화

  • PIC 컨트롤러는 I/O 포트 방식으로 연결
  • 마스터 PIC 컨트롤러는 0x20, 0x21을 사용하고 슬레이브 PIC 컨트롤러는 0xA0과 0xA1을 사용.
  • PIC 컨트롤러는 크게 두가지 타입의 커맨드를 제공.
    • ICW, 초기화와 관련된 명령.
    • OCW, 제어와 관련된 명령.
  • 해당 포트로 ICW1을 보내면 해당 포트에 쓰는 데이터는 ICW2, ICW3, ICW4 순서대로 해석.

11.2.2 인터럽트 입력 선택

void kMaskPICInterrupt(WORD wIRQBitmask) {
    // 마스터 PIC 컨트로러에 IMR 설정.
    kOutPortByte(PIC_MASTER_PORT2, (BYTE) wIRQBitmask);
    // 슬레이브 PIC 컨트롤러에 IMR 설정.
    kOutPortByte(PIC_MASTER_PORT2, (BYTE) (wIRQBitmask >> 8));
}
  • PIC 컨트롤러는 IRQ를 특정 벡터에 매핑하는 기능 외에 특정 인터럽트를 선택할 수 있는 기능도 있음.
  • OCW1를 이용해, IMR 레지스터에 무시할 인터럽트를 1로 설정.

11.2.3 인터럽트 종료 처리

void kSendEOIToPIC(int iIRQNumber) {
    kOutPortByte(PIC_MASTER_PORT1, 0x20);

    if(iIRQNumber >= 8) {
        kOutPortByte(PIC_SLAVE_PORT1, 0x20);
    }
}
  • 프로세서는 핸들러를 실행하여 인터럽트 처리를 완료한 후 다시 PIC 컨트롤러에 알려줘야 함.
  • 프로세서가 알려주지 않으면 PIC 컨르롤러는 핸들러가 수행중인 것으로 간주, 해당 인터럽트보다 우선순귀가 낮은 인터럽트를 실행하지 않음.

11.3 인터럽트, 예외 핸들러, 콘텍스트

11.3.1 임시 핸들러의 문제점

  • 임시 핸들러는 인터럽트나 예외처리 후 코드로 복귀했을 때 두 가지 문제로 정상적으로 코드를 수행할 수 없음.
    • 복귀할 때 사용하는 명령어를 사용하지 않음.
    • 프로세서의 상태를 완정히 저장 및 복원하지 않았다는 점.

11.3.2 콘텍스트 저장과 복원

  • 인터럽트 또는 예외로 인해 핸들러가 수행되거나 어떤 이유로 현재 수행 중인 코드를 중단하고 나서 다시 수행해야 한다면 전후 콘텍스트를 동일하게 유지해야 가능.
  • 스택에 프로세서가 저장하는 레지스터를 제외하고 저장.

11.3.3 인터럽트와 예외 핸들러 업그레이드

; 콘텍스트를 저장하고 셀렉터를 교체하는 매크로
%macro KSAVECONTEXT 0       ; 파라미터를 전달받지 않는 KSAVECONTEXT 매크로 정의
    ; RBP 레지스터부터 GS 세그먼트 셀렉터까지 모두 스택에 삽입
    push rbp
    mov rbp, rsp
    push rax
    push rbx
    push rcx
    push rdx
    push rdi
    push rsi
    push r8
    push r9
    push r10
    push r11
    push r12
    push r13
    push r14
    push r15

    mov ax, ds      ; DS 세그먼트 셀렉터와 ES 세그먼트 셀렉터는 스택에 직접
    push rax        ; 삽입할 수 없으므로, RAX 레지스터에 저장한 후 스택에 삽입
    mov ax, es
    push rax
    push fs
    push gs

    ; 세그먼트 셀렉터 교체
    mov ax, 0x10    ; AX 레지스터에 커널 데이터 세그먼트 디스크립터 저장
    mov ds, ax      ; DS 세그먼트 셀렉터부터 FS 세그먼트 셀렉터까지 모두
    mov es, ax      ; 커널 데이터 세그먼트로 교체
    mov gs, ax
    mov fs, ax
%endmacro           ; 매크로 끝

; 콘텍스트를 복원하는 매크로
%macro KLOADCONTEXT 0   ; 파라미터를 전달받지 않는 KSAVECONTEXT 매크로 정의
    ; GS 세그먼트 셀렉터부터 RBP 레지스터까지 모두 스택에서 꺼내 복원
    pop gs
    pop fs
    pop rax
    mov es, ax      ; ES 세그먼트 셀렉터와 DS 세그먼트 셀렉터는 스택에서 직접
    pop rax         ; 꺼내 복원할 수 없으므로, RAX 레지스터에 저장한 뒤에 복원
    mov ds, ax

    pop r15
    pop r14
    pop r13
    pop r12
    pop r11
    pop r10
    pop r9
    pop r8
    pop rsi
    pop rdi
    pop rdx
    pop rcx
    pop rbx
    pop rax
    pop rbp
%endmacro       ; 매크로 끝
  • ISR 함수는 인터럽트 처리를 위해 모두 16개를 작성해야하는데, 이를 위해 nasm에서 매크로를 정의.

11.3.4 IDT 테이블 수정

  • 이전 코드의 kDummyHandler 부분을 추가한 핸들러로 대체.

11.4 PIC 컨트롤러 제어 코드와 핸들러 코드의 통합과 빌드

11.4.1 PIC 컨트롤러 파일 추가

PIC.c 코드 보기;
PIC.h 코드 보기;

11.4.2 ISR 파일 추가

ISR.asm 코드 보기;
ISR.h 코드 보기;

11.4.3 인터럽트 핸들러 파일 추가

InterruptHandler.c 코드 보기;
InterruptHandler.h 코드 보기;

11.4.4 어셈블리어 유틸리티 파일 수정

AssemblyUtility.asm 코드 보기;
AssemblyUtility.h 코드 보기;

11.4.5 디스크립터 파일 수정

Descriptor.c 코드 보기;

11.4.6 C 언어 커널 엔트리 포인트 파일 수정

Main.c 코드 보기;

11.4.7 빌드와 실행

마치며

Share