x86: add irq support

main
anna 3 years ago
parent 89f3393b8b
commit 4679b7cee5
Signed by: fef
GPG Key ID: EC22E476DC2D3D84

@ -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);

@ -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 <gay/cdefs.h>
/** @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 <owo@fef.moe>.
*
* 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 <https://git.pixie.town/thufie/npl-builder>; 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.
*/

@ -2,8 +2,6 @@
#pragma once
#include <gay/types.h>
/** @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 <gay/cdefs.h>
#include <gay/types.h>
/** @brief Induce a brief CPU delay by writing zero to I/O port 0x80. */
extern void x86_io_wait(void);

@ -3,6 +3,8 @@
target_sources(gay_arch PRIVATE
idt.S
interrupt.c
irq.c
irq.S
port.S
trap.c
trap.S

@ -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);
}
/*

@ -0,0 +1,88 @@
/* See the end of this file for copyright and license terms. */
#include <arch/port.h>
#include <asm/common.h>
#include <gay/config.h>
.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 <owo@fef.moe>.
*
* 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 <https://git.pixie.town/thufie/npl-builder>; 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.
*/

@ -0,0 +1,111 @@
/* See the end of this file for copyright and license terms. */
#include <arch/irq.h>
#include <arch/port.h>
#include <arch/trap.h>
#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 <owo@fef.moe>.
*
* 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 <https://git.pixie.town/thufie/npl-builder>; 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.
*/

@ -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.

@ -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) */

@ -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@

@ -0,0 +1,62 @@
/* See the end of this file for copyright and license terms. */
#pragma once
#include <arch/irq.h>
#include <gay/types.h>
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 <owo@fef.moe>.
*
* 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 <https://git.pixie.town/thufie/npl-builder>; 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.
*/

@ -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

@ -0,0 +1,83 @@
/* See the end of this file for copyright and license terms. */
#include <arch/irq.h>
#include <gay/config.h>
#include <gay/errno.h>
#include <gay/irq.h>
#include <gay/kprintf.h>
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/<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 <owo@fef.moe>.
*
* 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 <https://git.pixie.town/thufie/npl-builder>; 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.
*/

@ -1,5 +1,7 @@
/* See the end of this file for copyright and license terms. */
#include <gay/irq.h>
/**
* @brief Main kernel entry point.
*
@ -13,6 +15,7 @@
*/
int main(int argc, char *argv[])
{
irq_init();
return 0;
}

Loading…
Cancel
Save