diff --git a/arch/x86/include/arch/interrupt.h b/arch/x86/include/arch/interrupt.h index d52469b..907a0a8 100644 --- a/arch/x86/include/arch/interrupt.h +++ b/arch/x86/include/arch/interrupt.h @@ -54,8 +54,12 @@ #define X86_VECT_VE 20 /** @brief Control Protection Exception */ #define X86_VECT_CP 21 -/* 22-31 are reserved, IRQs start at 32 */ +/* 22-31 are Intel reserved, IRQs start at 32 */ + +/** @brief First interrupt vector number for IRQs */ #define X86_VECT_IRQ_BASE 32 +/** @brief Interrupt vector number for IRQ `n` */ +#define X86_VECT_IRQ(n) (X86_VECT_IRQ_BASE + (n)) #ifndef _ASM_SOURCE @@ -115,9 +119,6 @@ struct x86_hw_frame { u16 user_ss; u16 _pad1; } __packed; -/** @brief Disable the Programmable Interrupt Controller. */ -void x86_disable_pic(void); - /** * @brief Set a gate handler in the Interrupt Descriptor Table. * @@ -135,6 +136,8 @@ void x86_set_gate(u8 vector, void (*handler)(void), u8 flags); */ void x86_set_trap_gate(u8 vector, void (*isr)(void)); +void x86_set_intr_gate(u8 vector, void (*isr)(void)); + /** @brief Set up the IDT and load it into IDTR. */ void x86_setup_interrupts(void); diff --git a/arch/x86/include/arch/irq.h b/arch/x86/include/arch/irq.h new file mode 100644 index 0000000..55b558d --- /dev/null +++ b/arch/x86/include/arch/irq.h @@ -0,0 +1,59 @@ +/* See the end of this file for copyright and license terms. */ + +#pragma once + +/** @brief Arch specific maximum number of IRQs (x86 8259 PIC version) */ +#define NUM_IRQ 16 + +#include + +/** @brief Low level interrupt controller init routine (x86 version). */ +void arch_irq_init(void); + +/** + * @brief Low level IRQ unmasking routine (x86 version). + * All of the sanity checks have already been done in `irq_enable()`, so this + * method expects the IRQ number to be valid and have an associated handler. + * + * @param number IRQ number + */ +void arch_irq_enable(unsigned int number); + +/** + * @brief Low level IRQ masking routine (x86 version). + * Like with `arch_irq_enable()`, this method does not do error checking. + * + * @param number IRQ number + */ +void arch_irq_disable(unsigned int number); + +/* generated in irq.S */ +extern void _x86_isr_irq0(void); +extern void _x86_isr_irq1(void); +extern void _x86_isr_irq3(void); +extern void _x86_isr_irq4(void); +extern void _x86_isr_irq5(void); +extern void _x86_isr_irq6(void); +extern void _x86_isr_irq7(void); +extern void _x86_isr_irq8(void); +extern void _x86_isr_irq9(void); +extern void _x86_isr_irq10(void); +extern void _x86_isr_irq11(void); +extern void _x86_isr_irq12(void); +extern void _x86_isr_irq13(void); +extern void _x86_isr_irq14(void); +extern void _x86_isr_irq15(void); + +/* + * This file is part of GayBSD. + * Copyright (c) 2021 fef . + * + * GayBSD is nonviolent software: you may only use, redistribute, and/or + * modify it under the terms of the Cooperative Nonviolent Public License + * (CNPL) as found in the LICENSE file in the source code root directory + * or at ; either version 7 + * of the license, or (at your option) any later version. + * + * GayBSD comes with ABSOLUTELY NO WARRANTY, to the extent + * permitted by applicable law. See the CNPL for details. + */ diff --git a/arch/x86/include/arch/port.h b/arch/x86/include/arch/port.h index 2ff1fa0..1d1bacf 100644 --- a/arch/x86/include/arch/port.h +++ b/arch/x86/include/arch/port.h @@ -2,8 +2,6 @@ #pragma once -#include - /** @brief I/O port number for the PIC1 command channel */ #define X86_PORT_PIC1_CMD 0x20 /** @brief I/O port number for the PIC1 data channel */ @@ -22,6 +20,7 @@ #ifndef _ASM_SOURCE #include +#include /** @brief Induce a brief CPU delay by writing zero to I/O port 0x80. */ extern void x86_io_wait(void); diff --git a/arch/x86/sys/CMakeLists.txt b/arch/x86/sys/CMakeLists.txt index 80bb044..c979f27 100644 --- a/arch/x86/sys/CMakeLists.txt +++ b/arch/x86/sys/CMakeLists.txt @@ -3,6 +3,8 @@ target_sources(gay_arch PRIVATE idt.S interrupt.c + irq.c + irq.S port.S trap.c trap.S diff --git a/arch/x86/sys/interrupt.c b/arch/x86/sys/interrupt.c index 161310b..de00988 100644 --- a/arch/x86/sys/interrupt.c +++ b/arch/x86/sys/interrupt.c @@ -10,7 +10,9 @@ void x86_setup_interrupts(void) { - x86_disable_pic(); + /* make sure all IRQs are masked first */ + x86_outb(X86_PORT_PIC1_DATA, 0xff); + x86_outb(X86_PORT_PIC2_DATA, 0xff); x86_set_trap_gate(X86_VECT_DE, _x86_isr_divide_error); x86_set_trap_gate(X86_VECT_DB, _x86_isr_debug_exception); @@ -56,10 +58,13 @@ void x86_set_trap_gate(u8 vector, void (*isr)(void)) kprintf("Tried to assign non-trap vector %d to a trap gate\n", vector); } -void x86_disable_pic(void) +void x86_set_intr_gate(u8 vector, void (*isr)(void)) { - x86_outb(X86_PORT_PIC2_DATA, 0xff); - x86_outb(X86_PORT_PIC1_DATA, 0xff); + u8 flags = X86_IDT_PRESENT | X86_IDT_GATE_INTR32 | X86_IDT_DPL(0); + if (vector < X86_VECT_IRQ_BASE) + kprintf("Tried to assign non-interrupt vector %d to an interrupt gate\n", vector); + else + x86_set_gate(vector, isr, flags); } /* diff --git a/arch/x86/sys/irq.S b/arch/x86/sys/irq.S new file mode 100644 index 0000000..1228e6b --- /dev/null +++ b/arch/x86/sys/irq.S @@ -0,0 +1,88 @@ +/* See the end of this file for copyright and license terms. */ + +#include + +#include + +#include + + .data + + /* void (*irq_table[NUM_IRQ])(void); */ + .extern irq_table + + /* there is probably a fancy CPU feature for this, but idk */ +_in_irq: + .byte 0 + + .text + + /* bool in_irq(void); */ +ASM_ENTRY(in_irq) + xor %eax, %eax + movb _in_irq, %al + ret +ASM_END(in_irq) + +.macro gen_irq num +ASM_ENTRY(_x86_isr_irq\num ) + push %eax + push %ecx + push %edx + movb $1, _in_irq +#if CFG_DEBUG_IRQ + pushl $\num +#endif + call *(irq_table + \num * 4) +#if CFG_DEBUG_IRQ + add $4, %esp +#endif + movb $0x20, %al /* PIC End Of Interrupt command code */ +.if \num >= 8 + jmp leave_irq_pic2 +.else + jmp leave_irq_pic1 +.endif +ASM_END(_x86_isr_irq\num ) +.endm + + gen_irq 0 + gen_irq 1 + /* IRQ 2 is for cascading from PIC2 to PIC1 */ + gen_irq 3 + gen_irq 4 + gen_irq 5 + gen_irq 6 + gen_irq 7 + gen_irq 8 + gen_irq 9 + gen_irq 10 + gen_irq 11 + gen_irq 12 + gen_irq 13 + gen_irq 14 + gen_irq 15 + +leave_irq_pic2: + outb %al, $X86_PORT_PIC2_CMD /* send End Of Interrupt to PIC2 */ +leave_irq_pic1: + outb %al, $X86_PORT_PIC1_CMD /* send End Of Interrupt to PIC1 */ + movb $0, _in_irq + pop %edx + pop %ecx + pop %eax + iret + +/* + * This file is part of GayBSD. + * Copyright (c) 2021 fef . + * + * GayBSD is nonviolent software: you may only use, redistribute, and/or + * modify it under the terms of the Cooperative Nonviolent Public License + * (CNPL) as found in the LICENSE file in the source code root directory + * or at ; either version 7 + * of the license, or (at your option) any later version. + * + * GayBSD comes with ABSOLUTELY NO WARRANTY, to the extent + * permitted by applicable law. See the CNPL for details. + */ diff --git a/arch/x86/sys/irq.c b/arch/x86/sys/irq.c new file mode 100644 index 0000000..033417a --- /dev/null +++ b/arch/x86/sys/irq.c @@ -0,0 +1,111 @@ +/* See the end of this file for copyright and license terms. */ + +#include +#include +#include + +#define ICW1_ICW4 0x01 /* ICW4 (not) needed */ +#define ICW1_SINGLE 0x02 /* Single (cascade) mode */ +#define ICW1_INTERVAL4 0x04 /* Call address interval 4 (8) */ +#define ICW1_LEVEL 0x08 /* Level triggered (edge) mode */ +#define ICW1_INIT 0x10 /* Initialization - required! */ + +#define ICW4_8086 0x01 /* 8086/88 (MCS-80/85) mode */ +#define ICW4_AUTO 0x02 /* Auto (normal) EOI */ +#define ICW4_BUF_SLAVE 0x08 /* Buffered mode/slave */ +#define ICW4_BUF_MASTER 0x0C /* Buffered mode/master */ +#define ICW4_SFNM 0x10 /* Special fully nested (not) */ + +void arch_irq_init(void) +{ + x86_set_intr_gate(X86_VECT_IRQ(0), _x86_isr_irq0); + x86_set_intr_gate(X86_VECT_IRQ(1), _x86_isr_irq1); + x86_set_intr_gate(X86_VECT_IRQ(3), _x86_isr_irq3); + x86_set_intr_gate(X86_VECT_IRQ(4), _x86_isr_irq4); + x86_set_intr_gate(X86_VECT_IRQ(5), _x86_isr_irq5); + x86_set_intr_gate(X86_VECT_IRQ(6), _x86_isr_irq6); + x86_set_intr_gate(X86_VECT_IRQ(7), _x86_isr_irq7); + x86_set_intr_gate(X86_VECT_IRQ(8), _x86_isr_irq8); + x86_set_intr_gate(X86_VECT_IRQ(9), _x86_isr_irq9); + x86_set_intr_gate(X86_VECT_IRQ(10), _x86_isr_irq10); + x86_set_intr_gate(X86_VECT_IRQ(11), _x86_isr_irq11); + x86_set_intr_gate(X86_VECT_IRQ(12), _x86_isr_irq12); + x86_set_intr_gate(X86_VECT_IRQ(13), _x86_isr_irq13); + x86_set_intr_gate(X86_VECT_IRQ(14), _x86_isr_irq14); + x86_set_intr_gate(X86_VECT_IRQ(15), _x86_isr_irq15); + + /* begin initialization sequence in cascade mode */ + x86_outb(X86_PORT_PIC1_CMD, ICW1_INIT | ICW1_ICW4); + x86_io_wait(); + x86_outb(X86_PORT_PIC2_CMD, ICW1_INIT | ICW1_ICW4); + x86_io_wait(); + + /* tell PIC1 its offset into the vector table */ + x86_outb(X86_PORT_PIC1_DATA, X86_VECT_IRQ_BASE); + x86_io_wait(); + /* tell PIC2 its offset into the vector table */ + x86_outb(X86_PORT_PIC2_DATA, X86_VECT_IRQ_BASE + 8); + x86_io_wait(); + + /* tell PIC1 that PIC2 is cascading into it on IRQ 2 */ + x86_outb(X86_PORT_PIC1_DATA, 1 << 2); + x86_io_wait(); + /* tell PIC2 that it is the second controller in the cascade chain */ + x86_outb(X86_PORT_PIC2_DATA, 2); + x86_io_wait(); + + /* don't ask me what the fuck this does */ + x86_outb(X86_PORT_PIC1_DATA, ICW4_8086); + x86_io_wait(); + x86_outb(X86_PORT_PIC2_DATA, ICW4_8086); + x86_io_wait(); + + /* mask all IRQs */ + x86_outb(X86_PORT_PIC1_DATA, 0xff); + x86_io_wait(); + x86_outb(X86_PORT_PIC2_DATA, 0xff); +} + +void arch_irq_enable(unsigned int number) +{ + u16 port; + if (number >= 8) { + port = X86_PORT_PIC2_DATA; + number -= 8; + } else { + port = X86_PORT_PIC1_DATA; + } + + u8 mask = x86_inb(port); + mask &= ~(1 << number); + x86_outb(port, mask); +} + +void arch_irq_disable(unsigned int number) +{ + u16 port; + if (number >= 8) { + port = X86_PORT_PIC2_DATA; + number -= 8; + } else { + port = X86_PORT_PIC1_DATA; + } + + u8 mask = x86_inb(port); + mask |= (1 << number); + x86_outb(port, mask); +} + +/* + * This file is part of GayBSD. + * Copyright (c) 2021 fef . + * + * GayBSD is nonviolent software: you may only use, redistribute, and/or + * modify it under the terms of the Cooperative Nonviolent Public License + * (CNPL) as found in the LICENSE file in the source code root directory + * or at ; either version 7 + * of the license, or (at your option) any later version. + * + * GayBSD comes with ABSOLUTELY NO WARRANTY, to the extent + * permitted by applicable law. See the CNPL for details. + */ diff --git a/cmake/config.cmake b/cmake/config.cmake index 78a559f..0d509b1 100644 --- a/cmake/config.cmake +++ b/cmake/config.cmake @@ -16,6 +16,8 @@ set(CFG_POISON_PAGES "Poison pages after allocate and free" ON) set(CFG_POISON_HEAP "Poison heap memory after kmalloc() and kfree()" ON) +set(CFG_DEBUG_IRQ "Debug IRQs" ON) + set(SCHED_MAX_TASKS "128" CACHE STRING "Maximum number of tasks") # This file is part of GayBSD. diff --git a/include/gay/cdefs.h b/include/gay/cdefs.h index e7f6cbb..0a4b87c 100644 --- a/include/gay/cdefs.h +++ b/include/gay/cdefs.h @@ -8,8 +8,6 @@ #ifdef __cplusplus #define __restrict -#else -#define __restrict restrict #endif #define __aligned(bytes) __attribute__(( aligned(bytes) )) @@ -24,7 +22,7 @@ #define __used __attribute__(( used )) -#ifdef __cplusplus__ +#ifdef __cplusplus #define __asmlink __used extern "C" #else /* this is mainly for IDEs w/out full assembly support (looking at you, CLion) */ diff --git a/include/gay/config.h.in b/include/gay/config.h.in index 0a609df..f5a01fd 100644 --- a/include/gay/config.h.in +++ b/include/gay/config.h.in @@ -34,6 +34,9 @@ /** @brief Poison heap areas after `kmalloc()` and `kfree()` */ #cmakedefine01 CFG_POISON_HEAP +/** @brief Debug IRQs */ +#cmakedefine01 CFG_DEBUG_IRQ + /** @brief Maximum number of tasks */ #define CFG_SCHED_MAX_TASKS @SCHED_MAX_TASKS@ diff --git a/include/gay/irq.h b/include/gay/irq.h new file mode 100644 index 0000000..0f18720 --- /dev/null +++ b/include/gay/irq.h @@ -0,0 +1,62 @@ +/* See the end of this file for copyright and license terms. */ + +#pragma once + +#include + +#include + +extern void (*irq_table[NUM_IRQ])(void); + +/** @brief Return true if you are in IRQ context. */ +bool in_irq(void); + +/** @brief Initialize the IRQ table and interrupt controller. */ +void irq_init(void); + +/** + * @brief Enable (unmask) an IRQ. + * + * @param number IRQ number + * @returns 0 on success, or a negative number if there is no associated handler + */ +int irq_enable(unsigned int number); + +/** + * @brief Disable (mask) an IRQ. + * + * @param number IRQ number + */ +void irq_disable(unsigned int number); + +/** + * @brief Attach a handler to a specific IRQ. + * + * @param number IRQ number + * @param handler IRQ handler + * @return 0 on success, or `-EEXIST` if the IRQ is already attached + * to another handler (use `irq_detach()` to, well, detach handlers + * that are no longer in use) + */ +int irq_attach(unsigned int number, void (*handler)(void)); + +/** + * @brief Detach an IRQ handler that was attached using `irq_attach()`. + * + * @param number IRQ number + */ +void irq_detach(unsigned int number); + +/* + * This file is part of GayBSD. + * Copyright (c) 2021 fef . + * + * GayBSD is nonviolent software: you may only use, redistribute, and/or + * modify it under the terms of the Cooperative Nonviolent Public License + * (CNPL) as found in the LICENSE file in the source code root directory + * or at ; either version 7 + * of the license, or (at your option) any later version. + * + * GayBSD comes with ABSOLUTELY NO WARRANTY, to the extent + * permitted by applicable law. See the CNPL for details. + */ diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt index b3af03e..64735e7 100644 --- a/kernel/CMakeLists.txt +++ b/kernel/CMakeLists.txt @@ -4,10 +4,11 @@ add_library(gay_kernel STATIC) target_include_directories(gay_kernel PRIVATE ${GAY_INCLUDE_DIRS}) target_compile_definitions(gay_kernel INTERFACE ${GAY_KERNEL_DEFINITIONS}) -target_link_libraries(gay_kernel PRIVATE c) +target_link_libraries(gay_kernel PRIVATE c gay_arch) target_sources(gay_kernel PRIVATE clist.c + irq.c kprintf.c main.c util.c diff --git a/kernel/irq.c b/kernel/irq.c new file mode 100644 index 0000000..9320643 --- /dev/null +++ b/kernel/irq.c @@ -0,0 +1,83 @@ +/* See the end of this file for copyright and license terms. */ + +#include + +#include +#include +#include +#include + +void (*irq_table[NUM_IRQ])(void); + +#if CFG_DEBUG_IRQ + /* + * If CFG_DEBUG_IRQ is set, the IRQ entry points pass the IRQ number + * as an argument to the IRQ handler (see arch//sys/irq.S). + */ + static void do_unknown_irq(unsigned int number) + { + kprintf("Unknown IRQ %d encountered!\n", number); + } +# define UNKNOWN_IRQ_HANDLER ((void (*)(void))do_unknown_irq) +#else + static void do_unknown_irq(void) + { + kprintf("Unknown IRQ encountered!\n"); + } +# define UNKNOWN_IRQ_HANDLER (do_unknown_irq) +#endif + +void irq_init(void) +{ + for (unsigned int i = 0; i < NUM_IRQ; i++) + irq_table[i] = UNKNOWN_IRQ_HANDLER; + arch_irq_init(); +} + +int irq_enable(unsigned int number) +{ + if (number >= NUM_IRQ) + return -ERANGE; + if (irq_table[number] == UNKNOWN_IRQ_HANDLER) + return -ENOENT; + + arch_irq_enable(number); + return 0; +} + +void irq_disable(unsigned int number) +{ + if (number < NUM_IRQ) + arch_irq_disable(number); +} + +int irq_attach(unsigned int number, void (*handler)(void)) +{ + if (number >= NUM_IRQ) + return -ERANGE; + if (irq_table[number] != UNKNOWN_IRQ_HANDLER) + return -EEXIST; + + irq_table[number] = handler; + return 0; +} + +void irq_detach(unsigned int number) +{ + if (number < NUM_IRQ) + irq_table[number] = UNKNOWN_IRQ_HANDLER; +} + +/* + * This file is part of GayBSD. + * Copyright (c) 2021 fef . + * + * GayBSD is nonviolent software: you may only use, redistribute, and/or + * modify it under the terms of the Cooperative Nonviolent Public License + * (CNPL) as found in the LICENSE file in the source code root directory + * or at ; either version 7 + * of the license, or (at your option) any later version. + * + * GayBSD comes with ABSOLUTELY NO WARRANTY, to the extent + * permitted by applicable law. See the CNPL for details. + */ diff --git a/kernel/main.c b/kernel/main.c index 99e3982..e5c790a 100644 --- a/kernel/main.c +++ b/kernel/main.c @@ -1,5 +1,7 @@ /* See the end of this file for copyright and license terms. */ +#include + /** * @brief Main kernel entry point. * @@ -13,6 +15,7 @@ */ int main(int argc, char *argv[]) { + irq_init(); return 0; }