Bladeren bron

lesson 19, IRQs

Carlos Fenollosa 10 jaren geleden
bovenliggende
commit
c2a05935f8

+ 46 - 0
19-interrupts-irqs/Makefile

@@ -0,0 +1,46 @@
+C_SOURCES = $(wildcard kernel/*.c drivers/*.c cpu/*.c)
+HEADERS = $(wildcard kernel/*.h drivers/*.h cpu/*.h)
+# Nice syntax for file extension replacement
+OBJ = ${C_SOURCES:.c=.o cpu/interrupt.o} 
+
+# Change this if your cross-compiler is somewhere else
+CC = /usr/local/i386elfgcc/bin/i386-elf-gcc
+GDB = /usr/local/i386elfgcc/bin/i386-elf-gdb
+# -g: Use debugging symbols in gcc
+CFLAGS = -g
+
+# First rule is run by default
+os-image.bin: boot/bootsect.bin kernel.bin
+	cat $^ > os-image.bin
+
+# '--oformat binary' deletes all symbols as a collateral, so we don't need
+# to 'strip' them manually on this case
+kernel.bin: boot/kernel_entry.o ${OBJ}
+	i386-elf-ld -o $@ -Ttext 0x1000 $^ --oformat binary
+
+# Used for debugging purposes
+kernel.elf: boot/kernel_entry.o ${OBJ}
+	i386-elf-ld -o $@ -Ttext 0x1000 $^ 
+
+run: os-image.bin
+	qemu-system-i386 -fda os-image.bin
+
+# Open the connection to qemu and load our kernel-object file with symbols
+debug: os-image.bin kernel.elf
+	qemu-system-i386 -s -fda os-image.bin -d guest_errors,int &
+	${GDB} -ex "target remote localhost:1234" -ex "symbol-file kernel.elf"
+
+# Generic rules for wildcards
+# To make an object, always compile from its .c
+%.o: %.c ${HEADERS}
+	${CC} ${CFLAGS} -ffreestanding -c $< -o $@
+
+%.o: %.asm
+	nasm $< -f elf -o $@
+
+%.bin: %.asm
+	nasm $< -f bin -o $@
+
+clean:
+	rm -rf *.bin *.dis *.o os-image.bin *.elf
+	rm -rf kernel/*.o boot/*.bin drivers/*.o boot/*.o cpu/*.o

+ 34 - 0
19-interrupts-irqs/README.md

@@ -0,0 +1,34 @@
+*Concepts you may want to Google beforehand: IRQs, PIC, polling*
+
+**Goal: Finish the interrupts implementation and CPU timer**
+
+Remap the PIC
+-------------
+
+When the CPU boots, the PIC maps IRQs 0-7 to INT 0x8-0xF
+and IRQs 8-15 to INT 0x70-0x77. This conflicts with the ISRs
+we programmed last lesson. Since we programmed ISRs 0-31, 
+it is standard to remap the IRQs to ISRs 32-47.
+
+The PICs are communicated with via I/O ports (see lesson 15).
+The Master PIC has command 0x20 and data 0x21, while the slave has
+command 0xA0 and data 0xA1.
+
+The code for remapping the PICs is obfuscated, so check 
+[this article](http://www.osdev.org/wiki/PIC) if you're curious.
+Otherwise, just look at `cpu/isr.c`, new code after we set the IDT
+gates for the ISRs. After that, we add the IDT gates for IRQs.
+
+Now we jump to assembler, at `interrupt.asm`. The first task is to
+add global definitions for the IRQ symbols we just used in the C code. 
+Look at the end of the `global` statements.
+
+Then, add the IRQ handlers. Same `interrupt.asm`, at the bottom. Notice
+how they jump to a new common stub: `irq_common_stub` (next step)
+
+We then create this `irq_common_stub` which is very similar to the ISR one.
+It is located at the top of `interrupt.asm`, and it also defines
+a new `[extern irq_handler]`
+
+Now back to C code.
+- Write the `irq_handler` in C: 

+ 1 - 0
19-interrupts-irqs/boot

@@ -0,0 +1 @@
+../18-interrupts/boot/

+ 17 - 0
19-interrupts-irqs/cpu/idt.c

@@ -0,0 +1,17 @@
+#include "idt.h"
+#include "../kernel/util.h"
+
+void set_idt_gate(int n, u32 handler) {
+    idt[n].low_offset = low_16(handler);
+    idt[n].sel = KERNEL_CS;
+    idt[n].always0 = 0;
+    idt[n].flags = 0x8E; 
+    idt[n].high_offset = high_16(handler);
+}
+
+void set_idt() {
+    idt_reg.base = (u32) &idt;
+    idt_reg.limit = IDT_ENTRIES * sizeof(idt_gate_t) - 1;
+    /* Don't make the mistake of loading &idt -- always load &idt_reg */
+    __asm__ __volatile__("lidtl (%0)" : : "r" (&idt_reg));
+}

+ 39 - 0
19-interrupts-irqs/cpu/idt.h

@@ -0,0 +1,39 @@
+#ifndef IDT_H
+#define IDT_H
+
+#include "types.h"
+
+/* Segment selectors */
+#define KERNEL_CS 0x08
+
+/* How every interrupt gate (handler) is defined */
+typedef struct {
+    u16 low_offset; /* Lower 16 bits of handler function address */
+    u16 sel; /* Kernel segment selector */
+    u8 always0;
+    /* First byte
+     * Bit 7: "Interrupt is present"
+     * Bits 6-5: Privilege level of caller (0=kernel..3=user)
+     * Bit 4: Set to 0 for interrupt gates
+     * Bits 3-0: bits 1110 = decimal 14 = "32 bit interrupt gate" */
+    u8 flags; 
+    u16 high_offset; /* Higher 16 bits of handler function address */
+} __attribute__((packed)) idt_gate_t ;
+
+/* A pointer to the array of interrupt handlers.
+ * Assembly instruction 'lidt' will read it */
+typedef struct {
+    u16 limit;
+    u32 base;
+} __attribute__((packed)) idt_register_t;
+
+#define IDT_ENTRIES 256
+idt_gate_t idt[IDT_ENTRIES];
+idt_register_t idt_reg;
+
+
+/* Functions implemented in idt.c */
+void set_idt_gate(int n, u32 handler);
+void set_idt();
+
+#endif

+ 425 - 0
19-interrupts-irqs/cpu/interrupt.asm

@@ -0,0 +1,425 @@
+; Defined in isr.c
+[extern isr_handler]
+[extern irq_handler]
+
+; Common ISR code
+isr_common_stub:
+    ; 1. Save CPU state
+	pusha ; Pushes edi,esi,ebp,esp,ebx,edx,ecx,eax
+	mov ax, ds ; Lower 16-bits of eax = ds.
+	push eax ; save the data segment descriptor
+	mov ax, 0x10  ; kernel data segment descriptor
+	mov ds, ax
+	mov es, ax
+	mov fs, ax
+	mov gs, ax
+	
+    ; 2. Call C handler
+	call isr_handler
+	
+    ; 3. Restore state
+	pop eax 
+	mov ds, ax
+	mov es, ax
+	mov fs, ax
+	mov gs, ax
+	popa
+	add esp, 8 ; Cleans up the pushed error code and pushed ISR number
+	sti
+	iret ; pops 5 things at once: CS, EIP, EFLAGS, SS, and ESP
+
+; Common IRQ code. Identical to ISR code except for the 'call' 
+; and the 'pop ebx'
+irq_common_stub:
+    pusha 
+    mov ax, ds
+    push eax
+    mov ax, 0x10
+    mov ds, ax
+    mov es, ax
+    mov fs, ax
+    mov gs, ax
+    call irq_handler ; Different than the ISR code
+    pop ebx  ; Different than the ISR code
+    mov ds, bx
+    mov es, bx
+    mov fs, bx
+    mov gs, bx
+    popa
+    add esp, 8
+    sti
+    iret 
+	
+; We don't get information about which interrupt was caller
+; when the handler is run, so we will need to have a different handler
+; for every interrupt.
+; Furthermore, some interrupts push an error code onto the stack but others
+; don't, so we will push a dummy error code for those which don't, so that
+; we have a consistent stack for all of them.
+
+; First make the ISRs global
+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
+; IRQs
+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 Exception
+isr0:
+    cli
+    push byte 0
+    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 (With Error Code!)
+isr8:
+    cli
+    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 (With Error Code!)
+isr10:
+    cli
+    push byte 10
+    jmp isr_common_stub
+
+; 11: Segment Not Present Exception (With Error Code!)
+isr11:
+    cli
+    push byte 11
+    jmp isr_common_stub
+
+; 12: Stack Fault Exception (With Error Code!)
+isr12:
+    cli
+    push byte 12
+    jmp isr_common_stub
+
+; 13: General Protection Fault Exception (With Error Code!)
+isr13:
+    cli
+    push byte 13
+    jmp isr_common_stub
+
+; 14: Page Fault Exception (With Error Code!)
+isr14:
+    cli
+    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 handlers
+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
+

+ 140 - 0
19-interrupts-irqs/cpu/isr.c

@@ -0,0 +1,140 @@
+#include "isr.h"
+#include "idt.h"
+#include "../drivers/screen.h"
+#include "../kernel/util.h"
+#include "../drivers/ports.h"
+
+isr_t interrupt_handlers[256];
+
+/* Can't do this with a loop because we need the address
+ * of the function names */
+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 the 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(); // Load with ASM
+}
+
+/* To print the message which defines every exception */
+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) {
+    /* If the irq involves the slave (IRQ > 7), send an EOI to it */
+    if (r.int_no >= 40) port_byte_out(0xA0, 0x20);
+    port_byte_out(0x20, 0x20); /* Send EOI to master */
+    /* Handle the interrupt in a more modular way */
+    if (interrupt_handlers[r.int_no] != 0) {
+        isr_t handler = interrupt_handlers[r.int_no];
+        handler(r);
+    }
+}

+ 88 - 0
19-interrupts-irqs/cpu/isr.h

@@ -0,0 +1,88 @@
+#ifndef ISR_H
+#define ISR_H
+
+#include "types.h"
+
+/* ISRs reserved for CPU exceptions */
+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 definitions */
+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
+
+/* Struct which aggregates many registers */
+typedef struct {
+   u32 ds; /* Data segment selector */
+   u32 edi, esi, ebp, esp, ebx, edx, ecx, eax; /* Pushed by pusha. */
+   u32 int_no, err_code; /* Interrupt number and error code (if applicable) */
+   u32 eip, cs, eflags, useresp, ss; /* Pushed by the processor automatically */
+} registers_t;
+
+void isr_install();
+void isr_handler(registers_t r);
+
+typedef void (*isr_t)(registers_t);
+void register_interrupt_handler(u8 n, isr_t handler);
+
+#endif

+ 16 - 0
19-interrupts-irqs/cpu/types.h

@@ -0,0 +1,16 @@
+#ifndef TYPES_H
+#define TYPES_H
+
+/* Instead of using 'chars' to allocate non-character bytes,
+ * we will use these new type with no semantic meaning */
+typedef unsigned int   u32;
+typedef          int   s32;
+typedef unsigned short u16;
+typedef          short s16;
+typedef unsigned char  u8;
+typedef          char  s8;
+
+#define low_16(address) (u16)((address) & 0xFFFF)
+#define high_16(address) (u16)(((address) >> 16) & 0xFFFF)
+
+#endif

+ 1 - 0
19-interrupts-irqs/drivers

@@ -0,0 +1 @@
+../18-interrupts/drivers/

+ 11 - 0
19-interrupts-irqs/kernel/kernel.c

@@ -0,0 +1,11 @@
+#include "../drivers/screen.h"
+#include "util.h"
+#include "../cpu/isr.h"
+#include "../cpu/idt.h"
+
+void main() {
+    isr_install();
+    /* Test the interrupts */
+    __asm__ __volatile__("int $2");
+    __asm__ __volatile__("int $3");
+}

+ 30 - 0
19-interrupts-irqs/kernel/util.c

@@ -0,0 +1,30 @@
+#include "util.h"
+
+void memory_copy(char *source, char *dest, int nbytes) {
+    int i;
+    for (i = 0; i < nbytes; i++) {
+        *(dest + i) = *(source + i);
+    }
+}
+
+void memory_set(u8 *dest, u8 val, u32 len) {
+    u8 *temp = (u8 *)dest;
+    for ( ; len != 0; len--) *temp++ = val;
+}
+
+/**
+ * K&R implementation
+ */
+void int_to_ascii(int n, char str[]) {
+    int i, sign;
+    if ((sign = n) < 0) n = -n;
+    i = 0;
+    do {
+        str[i++] = n % 10 + '0';
+    } while ((n /= 10) > 0);
+
+    if (sign < 0) str[i++] = '-';
+    str[i] = '\0';
+
+    /* TODO: implement "reverse" */
+}

+ 10 - 0
19-interrupts-irqs/kernel/util.h

@@ -0,0 +1,10 @@
+#ifndef UTIL_H
+#define UTIL_H
+
+#include "../cpu/types.h"
+
+void memory_copy(char *source, char *dest, int nbytes);
+void memory_set(u8 *dest, u8 val, u32 len);
+void int_to_ascii(int n, char str[]);
+
+#endif