aboutsummaryrefslogtreecommitdiff
path: root/kernel/include/main/interrupt.h
blob: 6a9ae007328e104403fba580d79aa693a283e3f8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
#pragma once

#include "kernel.h"
#include "types.h"
#include "util/debug.h"

// intr_disk_priamry/seconday so that they are different task priority classes
#define INTR_DIVIDE_BY_ZERO 0x00
#define INTR_INVALID_OPCODE 0x06
#define INTR_GPF 0x0d
#define INTR_PAGE_FAULT 0x0e

#define INTR_APICTIMER 0xf0
#define INTR_KEYBOARD 0xe0

#define INTR_DISK_PRIMARY 0xd0
#define INTR_SPURIOUS 0xfe
#define INTR_APICERR 0xff
#define INTR_SHUTDOWN 0xfd

/* NOTE: INTR_SYSCALL is not defined here, but is in syscall.h (it must be
 * in a userland-accessible header) */

// Intel Volume 3-A, 10.8.3.1 (10-29)
#define IPL_LOW 0
// we want to keep timer interrupts happening all the time to keep track of time
// :)
#define IPL_HIGH 0xe0
#define IPL_HIGHEST 0xff

typedef struct regs
{
    // all the regs
    uint64_t r_r15;
    uint64_t r_r14;
    uint64_t r_r13;
    uint64_t r_r12;
    uint64_t r_rbp;
    uint64_t r_rbx;
    uint64_t r_r11;
    uint64_t r_r10;
    uint64_t r_r9;
    uint64_t r_r8;
    uint64_t r_rax;
    uint64_t r_rcx;
    uint64_t r_rdx;
    uint64_t r_rsi;
    uint64_t r_rdi;

    // interrupt number
    uint64_t r_intr;

    // pushed by processor
    uint64_t r_err;
    uint64_t r_rip;
    uint64_t r_cs;
    uint64_t r_rflags;
    uint64_t r_rsp;
    uint64_t r_ss;
} packed regs_t;

void intr_init();

/* The function pointer which should be implemented by functions
 * which will handle interrupts. These handlers should be registered
 * with the interrupt subsystem via the intr_register function.
 * The regs structure contains the state of the registers saved when
 * the interrupt occured. Return whether or not the handler has itself
 * acknowledged the interrupt with a call to apic_eoi(). */
typedef long (*intr_handler_t)(regs_t *regs);

/* Registers an interrupt handler for the given interrupt handler.
 * If another handler had been previously registered for this interrupt
 * number it is returned, otherwise this function returns NULL. It
 * is good practice to assert that this function returns NULL unless
 * it is known that this will not be the case. */
intr_handler_t intr_register(uint8_t intr, intr_handler_t handler);

int32_t intr_map(uint16_t irq, uint8_t intr);

static inline uint64_t intr_enabled()
{
    uint64_t flags;
    __asm__ volatile("pushf; pop %0; and $0x200, %0;"
                     : "=r"(flags)::);
    return flags;
}

static inline void intr_enable() { __asm__ volatile("sti"); }

static inline void intr_disable() { __asm__ volatile("cli"); }

/* Atomically enables interrupts using the sti
 * instruction and puts the processor into a halted
 * state, this function returns once an interrupt
 * occurs. */
static inline void intr_wait()
{
    /* the sti instruction enables interrupts, however
     * interrupts are not checked for until the next
     * instruction is executed, this means that the following
     * code will not be succeptible to a bug where an
     * interrupt occurs between the sti and hlt commands
     * and does not wake us up from the hlt. */
    __asm__ volatile("sti; hlt");
}

/* Sets the interrupt priority level for hardware interrupts.
 * At initialization time devices should detect their individual
 * IPLs and save them for use with this function. IPL_LOW allows
 * all hardware interrupts. IPL_HIGH blocks all hardware interrupts */
uint8_t intr_setipl(uint8_t ipl);

/* Retreives the current interrupt priority level. */
uint8_t intr_getipl();

void dump_registers(regs_t *regs);