ABOUT ME

-

  • [OS] 19. Interrupt-irqs
    OS/OS from Scratch 2021. 7. 12. 00:57

    서론

    이번 강의에서는 Interrupt Request를 구현하겠습니다. 해당 Github 강의는 다음과 같습니다.

    https://github.com/cfenollosa/os-tutorial/tree/master/19-interrupts-irqs

     

    cfenollosa/os-tutorial

    How to create an OS from scratch. Contribute to cfenollosa/os-tutorial development by creating an account on GitHub.

    github.com


    이론

    Computer Buses

    버스에 대한 좋은 동영상이 있어 첨부합니다.

    Computer Bus

    버스는 데이터 버스, 주소 버스, 제어 버스로 나뉩니다.

    데이터 버스, 주소 버스, 제어 버스

    • 데이터 버스: 데이터 버스는 데이터가 이동할 수 있도록 하며 32, 64개의 선들로 구성되어 있다.
    • 주소 버스: 데이터의 출발지나 도착지의 메모리 주소를 전달한다.
    • 제어 버스: 데이터 버스와 주소 버스를 제어한다.
      • Read
      • Write
      • Transfer Acknowledgment (데이터가 잘 보내졌는지)
      • Clock Signal (언제 데이터 보냈는지 표시)

    데이터가 전송되는 방식에는 Serial, Parallel 방식이 있습니다. 현대 CPU의 대부분의 경우 Serial 방식을 사용합니다.

    • Serial: 데이터가 순차적으로 하나의 Channel을 통해 전송된다.
    • Parallel: 데이터가 여러 Channel을 통해 동시에 전송된다. 이때 각 Channel을 통해 들어오는 데이터가 동시에 도달해야 한다.

    IRQ (Interrupt Request)

    IRQ는 컴퓨터의 주변 기기가 CPU에게 어떤 이벤트가 발생했다는 것을 알리는 line입니다. 즉, CPU가 어떤 일이 처리하다가도 주변 기기에서 Interrupt가 들어오면 하던 일을 중단하고 해당 Interrupt을 처리하게 됩니다. IRQ 번호에 따른 Interrupt 내용은 다음과 같습니다.

    0 IRQ 타이머
    1 키보드
    2 IRQ 캐스 케이드 공유
    3 시리얼포트 2, COM2, COM4 ( 모뎀 / 마우스)
    4 시리얼포트 1, COM1, COM3 ( 모뎀 / 마우스)
    5 LPT2 병렬포트 혹은
    6 FDD 컨트롤러
    7 LPT1 병열 포트 등 혹은 사운드카드
    8 RTC( Real Time Clock)
    9 예비, 주로 미디 카드(MPU401)에서 사용 , SOUND ,VGA . USB, MPEG II
    10 예비 , SOUND ,VGA . USB,MPEG II,
    11 예비, SCSI 아답터, SOUND ,VGA . USB,MPEG II
    12 PS/2 마우스
    13 코프로세서 (수치처리보조연산자 )
    14 IDE 하드 컨트롤러 Primary
    15 IDE 하드 컨트롤러 Secondary

    PCI (Peripheral Component Interconnect)

    PCI는 CPU와 컴퓨터 주변기기를 연결하는 Local Bus입니다. PCI는 Bridge, Master, Slave으로 구분됩니다.

    • Bridge: 시스템과 PCI 버스를 연결해준다
    • Master: 버스 전송을 초기화하고 주소, 데이터 전송, 제어 신호들을 조정한다
    • Slave: 마스터에 의존하는 수동적인 메모리 디바이스이다

    CPU가 boot 되면 PIC는 IRQ 0-7을 INT 0x8-0xF에, IRQ 8-15을 INT 0x70-0x77에 맵핑합니다. 지난 강의들에서 ISR를 0-31에 맵핑하였으므로 IRQ를 ISR 32-47에 맵핑해야 겹치지 않습니다.

    PIC는 I/O port을 통해 통신하고, 이때 Master PIC는 0x20을 통해 명령, 0x21을 통해 데이터를 처리합니다. Slave PIC는 0xA0을 통해 명령, 0xA1을 통해 데이터를 처리합니다.


    코드

    이번 강의의 코드는 지난 강의에서 ISR을 설정했던 것과 같은 방법으로 IRQ를 설정해주는 것이 전부입니다. 설명은 주석으로 대체하겠습니다.

    cpu/interrupt.asm

    ; isr.c의 isr_handler() & irq_handler()
    [extern isr_handler]
    [extern irq_handler]
    
    ; Common ISR Code
    isr_common_stub:
        ; 1. CPU상태 저장
        ; (인자) edi, esi, ebp, esp, ebx, edx, ecx, eax push 
        pusha
        ; EAX의 하위 16bit = ds
        mov ax, ds
        ; (인자) Data Segment Descriptor 저장
        push eax
        ; Kernel Data Segment Descriptor
        mov ax, 0x10
        mov ds, ax
        mov es, ax
        mov fs, ax
        mov gs, ax
    
        ; 2. C handler 호출
        call isr_handler
    
        ; 3. 원래 상태로 되돌려놓음
        pop eax
        mov ds, ax
        mov es, ax
        mov fs, ax
        mov gs, ax
        popa
        ; push된 에러코드와 ISR 번호 삭제
        add esp, 8
        sti
        ; pop cs, eip, eflags, ss, esp
        iret
    
    ; Common IRQ Code
    irq_common_stub:
        ; 1. CPU상태 저장
        pusha
        mov ax, ds
        push eax
        mov ax, 0x10
        mov ds, ax
        mov es, ax
        mov fs, ax
        mov gs, ax
    
        ; 2. C handler 호출
        call irq_handler
    
        ; 3. 원래 상태로 되돌려놓음
        pop ebx
        mov ds, bx
        mov es, bx
        mov fs, bx
        mov gs, bx
        popa
        add esp, 8
        sti
        iret
    
    ; 여러 handler에 대한 interrupt handler 정의
    ; 일부 interrupt는 에러코드를 stack에 저장
    ; ISR
    global isr0
    global isr1
    global isr2
    global isr3
    global isr4
    global isr5
    global isr6
    global isr7
    global isr8
    global isr9
    global isr10
    global isr11
    global isr12
    global isr13
    global isr14
    global isr15
    global isr16
    global isr17
    global isr18
    global isr19
    global isr20
    global isr21
    global isr22
    global isr23
    global isr24
    global isr25
    global isr26
    global isr27
    global isr28
    global isr29
    global isr30
    global isr31
    ; IRQ
    global irq0
    global irq1
    global irq2
    global irq3
    global irq4
    global irq5
    global irq6
    global irq7
    global irq8
    global irq9
    global irq10
    global irq11
    global irq12
    global irq13
    global irq14
    global irq15
    
    ; 0: Divide by zero division
    isr0:
        cli
        ; (인자) 에러 코드 (더미)
        push byte 0
        ; (인자) interrupt 번호
        push byte 0
        jmp isr_common_stub
    
    ; 1: Debug Exception
    isr1:
        cli
        push byte 0
        push byte 1
        jmp isr_common_stub
    
    ; 2: Non Maskable Interrupt Exception
    isr2:
        cli
        push byte 0
        push byte 2
        jmp isr_common_stub
    
    ; 3: Int 3 Exception
    isr3:
        cli
        push byte 0
        push byte 3
        jmp isr_common_stub
    
    ; 4: INTO Exception
    isr4:
        cli
        push byte 0
        push byte 4
        jmp isr_common_stub
    
    ; 5: Out of Bounds Exception
    isr5:
        cli
        push byte 0
        push byte 5
        jmp isr_common_stub
    
    ; 6: Invalid Opcode Exception
    isr6:
        cli
        push byte 0
        push byte 6
        jmp isr_common_stub
    
    ; 7: Coprocessor Not Available Exception
    isr7:
        cli
        push byte 0
        push byte 7
        jmp isr_common_stub
    
    ; 8: Double Fault Exception
    isr8:
        cli
        ; (인자) 에러 코드 Stack에 이미 push됨
        push byte 8
        jmp isr_common_stub
    
    ; 9: Coprocessor Segment Overrun Exception
    isr9:
        cli
        push byte 0
        push byte 9
        jmp isr_common_stub
    
    ; 10: Bad TSS Exception
    isr10:
        cli
        ; (인자) 에러 코드 Stack에 이미 push됨
        push byte 10
        jmp isr_common_stub
    
    ; 11: Segment Not Present Exception
    isr11:
        cli
        ; (인자) 에러 코드 Stack에 이미 push됨
        push byte 11
        jmp isr_common_stub
    
    ; 12: Stack Fault Exception
    isr12:
        cli
        ; (인자) 에러 코드 Stack에 이미 push됨
        push byte 12
        jmp isr_common_stub
    
    ; 13: General Protection Fault Exception
    isr13:
        cli
        ; (인자) 에러 코드 Stack에 이미 push됨
        push byte 13
        jmp isr_common_stub
    
    ; 14: Page Fault Exception
    isr14:
        cli
        ; (인자) 에러 코드 Stack에 이미 push됨
        push byte 14
        jmp isr_common_stub
    
    ; 15: Reserved Exception
    isr15:
        cli
        push byte 0
        push byte 15
        jmp isr_common_stub
    
    ; 16: Floating Point Exception
    isr16:
        cli
        push byte 0
        push byte 16
        jmp isr_common_stub
    
    ; 17: Alignment Check Exception
    isr17:
        cli
        push byte 0
        push byte 17
        jmp isr_common_stub
    
    ; 18: Machine Check Exception
    isr18:
        cli
        push byte 0
        push byte 18
        jmp isr_common_stub
    
    ; 19: Reserved
    isr19:
        cli
        push byte 0
        push byte 19
        jmp isr_common_stub
    
    ; 20: Reserved
    isr20:
        cli
        push byte 0
        push byte 20
        jmp isr_common_stub
    
    ; 21: Reserved
    isr21:
        cli
        push byte 0
        push byte 21
        jmp isr_common_stub
    
    ; 22: Reserved
    isr22:
        cli
        push byte 0
        push byte 22
        jmp isr_common_stub
    
    ; 23: Reserved
    isr23:
        cli
        push byte 0
        push byte 23
        jmp isr_common_stub
    
    ; 24: Reserved
    isr24:
        cli
        push byte 0
        push byte 24
        jmp isr_common_stub
    
    ; 25: Reserved
    isr25:
        cli
        push byte 0
        push byte 25
        jmp isr_common_stub
    
    ; 26: Reserved
    isr26:
        cli
        push byte 0
        push byte 26
        jmp isr_common_stub
    
    ; 27: Reserved
    isr27:
        cli
        push byte 0
        push byte 27
        jmp isr_common_stub
    
    ; 28: Reserved
    isr28:
        cli
        push byte 0
        push byte 28
        jmp isr_common_stub
    
    ; 29: Reserved
    isr29:
        cli
        push byte 0
        push byte 29
        jmp isr_common_stub
    
    ; 30: Reserved
    isr30:
        cli
        push byte 0
        push byte 30
        jmp isr_common_stub
    
    ; 31: Reserved
    isr31:
        cli
        push byte 0
        push byte 31
        jmp isr_common_stub
    
    ; IRQ Handler
    irq0:
    	cli
    	push byte 0
    	push byte 32
    	jmp irq_common_stub
    
    irq1:
    	cli
    	push byte 1
    	push byte 33
    	jmp irq_common_stub
    
    irq2:
    	cli
    	push byte 2
    	push byte 34
    	jmp irq_common_stub
    
    irq3:
    	cli
    	push byte 3
    	push byte 35
    	jmp irq_common_stub
    
    irq4:
    	cli
    	push byte 4
    	push byte 36
    	jmp irq_common_stub
    
    irq5:
    	cli
    	push byte 5
    	push byte 37
    	jmp irq_common_stub
    
    irq6:
    	cli
    	push byte 6
    	push byte 38
    	jmp irq_common_stub
    
    irq7:
    	cli
    	push byte 7
    	push byte 39
    	jmp irq_common_stub
    
    irq8:
    	cli
    	push byte 8
    	push byte 40
    	jmp irq_common_stub
    
    irq9:
    	cli
    	push byte 9
    	push byte 41
    	jmp irq_common_stub
    
    irq10:
    	cli
    	push byte 10
    	push byte 42
    	jmp irq_common_stub
    
    irq11:
    	cli
    	push byte 11
    	push byte 43
    	jmp irq_common_stub
    
    irq12:
    	cli
    	push byte 12
    	push byte 44
    	jmp irq_common_stub
    
    irq13:
    	cli
    	push byte 13
    	push byte 45
    	jmp irq_common_stub
    
    irq14:
    	cli
    	push byte 14
    	push byte 46
    	jmp irq_common_stub
    
    irq15:
    	cli
    	push byte 15
    	push byte 47
    	jmp irq_common_stub

    cpu/isr.h

    #ifndef ISR_H
    #define ISR_H
    
    #include "types.h"
    
    // CPU Exception들
    extern void isr0();
    extern void isr1();
    extern void isr2();
    extern void isr3();
    extern void isr4();
    extern void isr5();
    extern void isr6();
    extern void isr7();
    extern void isr8();
    extern void isr9();
    extern void isr10();
    extern void isr11();
    extern void isr12();
    extern void isr13();
    extern void isr14();
    extern void isr15();
    extern void isr16();
    extern void isr17();
    extern void isr18();
    extern void isr19();
    extern void isr20();
    extern void isr21();
    extern void isr22();
    extern void isr23();
    extern void isr24();
    extern void isr25();
    extern void isr26();
    extern void isr27();
    extern void isr28();
    extern void isr29();
    extern void isr30();
    extern void isr31();
    
    // IRQ
    extern void irq0();
    extern void irq1();
    extern void irq2();
    extern void irq3();
    extern void irq4();
    extern void irq5();
    extern void irq6();
    extern void irq7();
    extern void irq8();
    extern void irq9();
    extern void irq10();
    extern void irq11();
    extern void irq12();
    extern void irq13();
    extern void irq14();
    extern void irq15();
    
    #define IRQ0 32
    #define IRQ1 33
    #define IRQ2 34
    #define IRQ3 35
    #define IRQ4 36
    #define IRQ5 37
    #define IRQ6 38
    #define IRQ7 39
    #define IRQ8 40
    #define IRQ9 41
    #define IRQ10 42
    #define IRQ11 43
    #define IRQ12 44
    #define IRQ13 45
    #define IRQ14 46
    #define IRQ15 47
    
    // 여러 레지스터 포함한 구조체
    typedef struct {
        // Data Segment
        u32 ds;
        // pusha로 pushed되는 레지스터
        u32 edi, esi, ebp, esp, ebx, edx, ecx, eax;
        // Interrupt 번호와 에러 코드
        u32 int_no, err_code;
        // CPU에 의해 자동적으로 pushed 됨
        u32 eip, cs, eflags, useresp, ss;
    } registers_t;
    
    void isr_install();
    void isr_handler(registers_t r);
    
    // isr_t: registers_t를 인자로 받는 void 함수 포인터 
    typedef void (*isr_t)(registers_t);
    void register_interrupt_handler(u8 n, isr_t handler);
    
    #endif

    cpu/isr.c

    #include "isr.h"
    #include "idt.h"
    #include "../drivers/screen.h"
    #include "../kernel/util.h"
    #include "../drivers/ports.h"
    
    isr_t interrupt_handlers[256];
    
    void isr_install() {
        set_idt_gate(0, (u32)isr0);
        set_idt_gate(1, (u32)isr1);
        set_idt_gate(2, (u32)isr2);
        set_idt_gate(3, (u32)isr3);
        set_idt_gate(4, (u32)isr4);
        set_idt_gate(5, (u32)isr5);
        set_idt_gate(6, (u32)isr6);
        set_idt_gate(7, (u32)isr7);
        set_idt_gate(8, (u32)isr8);
        set_idt_gate(9, (u32)isr9);
        set_idt_gate(10, (u32)isr10);
        set_idt_gate(11, (u32)isr11);
        set_idt_gate(12, (u32)isr12);
        set_idt_gate(13, (u32)isr13);
        set_idt_gate(14, (u32)isr14);
        set_idt_gate(15, (u32)isr15);
        set_idt_gate(16, (u32)isr16);
        set_idt_gate(17, (u32)isr17);
        set_idt_gate(18, (u32)isr18);
        set_idt_gate(19, (u32)isr19);
        set_idt_gate(20, (u32)isr20);
        set_idt_gate(21, (u32)isr21);
        set_idt_gate(22, (u32)isr22);
        set_idt_gate(23, (u32)isr23);
        set_idt_gate(24, (u32)isr24);
        set_idt_gate(25, (u32)isr25);
        set_idt_gate(26, (u32)isr26);
        set_idt_gate(27, (u32)isr27);
        set_idt_gate(28, (u32)isr28);
        set_idt_gate(29, (u32)isr29);
        set_idt_gate(30, (u32)isr30);
        set_idt_gate(31, (u32)isr31);
    
        // Remap PIC (설명 추가 필요)
        port_byte_out(0x20, 0x11);
        port_byte_out(0xA0, 0x11);
        port_byte_out(0x21, 0x20);
        port_byte_out(0xA1, 0x28);
        port_byte_out(0x21, 0x04);
        port_byte_out(0xA1, 0x02);
        port_byte_out(0x21, 0x01);
        port_byte_out(0xA1, 0x01);
        port_byte_out(0x21, 0x0);
        port_byte_out(0xA1, 0x0); 
    
        // Install the IRQs
        set_idt_gate(32, (u32)irq0);
        set_idt_gate(33, (u32)irq1);
        set_idt_gate(34, (u32)irq2);
        set_idt_gate(35, (u32)irq3);
        set_idt_gate(36, (u32)irq4);
        set_idt_gate(37, (u32)irq5);
        set_idt_gate(38, (u32)irq6);
        set_idt_gate(39, (u32)irq7);
        set_idt_gate(40, (u32)irq8);
        set_idt_gate(41, (u32)irq9);
        set_idt_gate(42, (u32)irq10);
        set_idt_gate(43, (u32)irq11);
        set_idt_gate(44, (u32)irq12);
        set_idt_gate(45, (u32)irq13);
        set_idt_gate(46, (u32)irq14);
        set_idt_gate(47, (u32)irq15);
    
        set_idt();
    }
    
    char *exception_messages[] = {
        "Division By Zero",
        "Debug",
        "Non Maskable Interrupt",
        "Breakpoint",
        "Into Detected Overflow",
        "Out of Bounds",
        "Invalid Opcode",
        "No Coprocessor",
    
        "Double Fault",
        "Coprocessor Segment Overrun",
        "Bad TSS",
        "Segment Not Present",
        "Stack Fault",
        "General Protection Fault",
        "Page Fault",
        "Unknown Interrupt",
    
        "Coprocessor Fault",
        "Alignment Check",
        "Machine Check",
        "Reserved",
        "Reserved",
        "Reserved",
        "Reserved",
        "Reserved",
    
        "Reserved",
        "Reserved",
        "Reserved",
        "Reserved",
        "Reserved",
        "Reserved",
        "Reserved",
        "Reserved"
    };
    
    void isr_handler(registers_t r) {
        kprint("Received Interrupt: ");
        char s[3];
        int_to_ascii(r.int_no, s);
        kprint(s);
        kprint("\n");
        kprint(exception_messages[r.int_no]);
        kprint("\n");
    }
    
    void register_interrupt_handler(u8 n, isr_t handler) {
        interrupt_handlers[n] = handler;
    }
    
    void irq_handler(registers_t r) {
        // Interrupt를 받은 후 PIC에 EOI(End of Interrupt)을 전송해야
        // 다시 Interrupt를 보내지 않는다
        // EOI Interrupt: 0x20
        if(r.int_no>=40) { // Slave PCI
            port_byte_out(0xA0, 0x20);
        }
        // Master PCI
        port_byte_out(0x20, 0x20);
    
        if(interrupt_handlers[r.int_no]!=0) {
            isr_t handler = interrupt_handlers[r.int_no];
            handler(r);
        }
    }

    실행 결과

    다음 강의에서 구현한 IRQ들을 테스트해보겠습니다.

     

     

    'OS > OS from Scratch' 카테고리의 다른 글

    [OS] 21. Shell  (0) 2021.07.23
    [OS] 20. Interrupt-timer  (0) 2021.07.18
    [OS] 18. Interrupts  (0) 2021.07.03
    [OS] 17. Video Scroll  (0) 2021.07.02
    [OS] 16. Video Driver  (0) 2021.07.02
Designed by Tistory.