-
[OS] 19. Interrupt-irqsOS/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