| 1 | /* @title: IRQs */ |
| 2 | #pragma once |
| 3 | #include <console/crash.h> |
| 4 | #include <kassert.h> |
| 5 | #include <smp/core.h> |
| 6 | #include <stdbool.h> |
| 7 | #include <stdint.h> |
| 8 | #include <structures/cpu_mask.h> |
| 9 | #include <structures/list.h> |
| 10 | #include <types/types.h> |
| 11 | |
| 12 | #define IRQ_DIV_BY_Z 0x0 |
| 13 | #define IRQ_DEBUG 0x1 |
| 14 | #define IRQ_NMI 0x2 |
| 15 | #define IRQ_BREAKPOINT 0x3 |
| 16 | #define IRQ_DBF 0x8 |
| 17 | #define IRQ_SSF 0xC |
| 18 | #define IRQ_GPF 0xD |
| 19 | #define IRQ_PAGE_FAULT 0xE |
| 20 | #define IRQ_TIMER 0x20 |
| 21 | #define IRQ_SCHEDULER IRQ_TIMER |
| 22 | #define IRQ_TLB_SHOOTDOWN 0x22 |
| 23 | #define IRQ_NOP 0x24 |
| 24 | #define IRQ_DPC 0x25 |
| 25 | #define IRQ_EXCEPTION_COUNT 32 |
| 26 | |
| 27 | struct irq_context; |
| 28 | struct irq_desc; |
| 29 | |
| 30 | enum irq_result { |
| 31 | IRQ_NONE = 0, |
| 32 | IRQ_HANDLED = 1, |
| 33 | }; |
| 34 | |
| 35 | typedef enum irq_result (*irq_handler_t)(void *ctx, uint8_t vector, |
| 36 | struct irq_context *ictx); |
| 37 | |
| 38 | enum irq_flags { |
| 39 | IRQ_FLAG_SHARED = 1, /* IRQ is shared between things */ |
| 40 | IRQ_FLAG_LEVEL_TRIGGERED = 1 << 1, |
| 41 | IRQ_FLAG_EDGE_TRIGGERED = 1 << 2, |
| 42 | IRQ_FLAG_NONE = 0, |
| 43 | }; |
| 44 | |
| 45 | struct irq_action { |
| 46 | irq_handler_t handler; |
| 47 | void *data; |
| 48 | struct list_head list; |
| 49 | }; |
| 50 | |
| 51 | struct irq_chip { |
| 52 | const char *name; |
| 53 | |
| 54 | void (*mask)(struct irq_desc *); |
| 55 | void (*unmask)(struct irq_desc *); |
| 56 | void (*eoi)(struct irq_desc *); |
| 57 | |
| 58 | void (*set_affinity)(struct irq_desc *, struct cpu_mask *); |
| 59 | int (*set_rate_limit)(struct irq_desc *, time_us_t interval); |
| 60 | }; |
| 61 | |
| 62 | struct irq_desc { |
| 63 | uint8_t vector; |
| 64 | enum irq_flags flags; |
| 65 | |
| 66 | const char *name; |
| 67 | |
| 68 | struct irq_chip *chip; |
| 69 | void *chip_data; |
| 70 | |
| 71 | struct list_head actions; |
| 72 | |
| 73 | struct cpu_mask affinity; |
| 74 | struct cpu_mask masked_cpus; |
| 75 | |
| 76 | bool present; /* Have we set handlers? */ |
| 77 | bool allocated; /* Has this been allocated? */ |
| 78 | bool enabled; |
| 79 | }; |
| 80 | |
| 81 | struct irq_context { |
| 82 | uint64_t rax; |
| 83 | uint64_t rbx; |
| 84 | uint64_t rcx; |
| 85 | uint64_t rdx; |
| 86 | uint64_t rbp; |
| 87 | uint64_t rdi; |
| 88 | uint64_t rsi; |
| 89 | uint64_t r8; |
| 90 | uint64_t r9; |
| 91 | uint64_t r10; |
| 92 | uint64_t r11; |
| 93 | uint64_t r12; |
| 94 | uint64_t r13; |
| 95 | uint64_t r14; |
| 96 | uint64_t r15; |
| 97 | uint64_t error_code; |
| 98 | uint64_t rip; |
| 99 | uint64_t cs; |
| 100 | uint64_t rflags; |
| 101 | uint64_t rsp; |
| 102 | uint64_t ss; |
| 103 | }; |
| 104 | |
| 105 | static inline void irq_context_to_crash_regs(const struct irq_context *ictx, |
| 106 | struct crash_regs *out) { |
| 107 | if (!ictx || !out) |
| 108 | return; |
| 109 | out->rip = ictx->rip; |
| 110 | out->rflags = ictx->rflags; |
| 111 | __asm__ volatile("mov %%cr2, %0" : "=r" (out->cr2)); |
| 112 | __asm__ volatile("mov %%cr3, %0" : "=r" (out->cr3)); |
| 113 | out->rax = ictx->rax; |
| 114 | out->rbx = ictx->rbx; |
| 115 | out->rcx = ictx->rcx; |
| 116 | out->rdx = ictx->rdx; |
| 117 | out->rbp = ictx->rbp; |
| 118 | out->rdi = ictx->rdi; |
| 119 | out->rsi = ictx->rsi; |
| 120 | out->r8 = ictx->r8; |
| 121 | out->r9 = ictx->r9; |
| 122 | out->r10 = ictx->r10; |
| 123 | out->r11 = ictx->r11; |
| 124 | out->r12 = ictx->r12; |
| 125 | out->r13 = ictx->r13; |
| 126 | out->r14 = ictx->r14; |
| 127 | out->r15 = ictx->r15; |
| 128 | out->rsp = ictx->rsp; |
| 129 | } |
| 130 | |
| 131 | void irq_register(char *name, uint8_t vector, irq_handler_t handler, void *ctx, |
| 132 | enum irq_flags flags); |
| 133 | void irq_register_full(struct irq_desc *d); |
| 134 | void irq_set_chip(uint8_t vector, struct irq_chip *chip, void *data); |
| 135 | |
| 136 | /* We DO NOT set _IRQ here because _NONE is used as |
| 137 | * this set of functions will set the flags _NONE checks */ |
| 138 | static inline uint32_t irq_mark_self_in_interrupt(bool new) { |
| 139 | uint32_t old = smp_ctx_irq_count(smp_ctx: smp_ctx(c: TOPC_NONE)); |
| 140 | if (new) |
| 141 | smp_ctx_add(c: TOPC_NONE, SMP_CTX_IRQ_ONE, SMP_CTX_IRQ_MASK); |
| 142 | else |
| 143 | smp_ctx_sub(c: TOPC_NONE, SMP_CTX_IRQ_ONE, SMP_CTX_IRQ_MASK); |
| 144 | return old; |
| 145 | } |
| 146 | |
| 147 | static inline uint32_t irq_mark_self_in_nmi(bool new) { |
| 148 | uint32_t old = smp_ctx_nmi_count(smp_ctx: smp_ctx(c: TOPC_NONE)); |
| 149 | if (new) |
| 150 | smp_ctx_add(c: TOPC_NONE, SMP_CTX_NMI_ONE, SMP_CTX_NMI_MASK); |
| 151 | else |
| 152 | smp_ctx_sub(c: TOPC_NONE, SMP_CTX_NMI_ONE, SMP_CTX_NMI_MASK); |
| 153 | return old; |
| 154 | } |
| 155 | |
| 156 | /* These are called from the verification logic, |
| 157 | * they are simple reads */ |
| 158 | static inline bool irq_in_nmi(void) { |
| 159 | return (smp_ctx(c: TOPC_NONE) & SMP_CTX_NMI_MASK) != 0; |
| 160 | } |
| 161 | |
| 162 | static inline bool irq_in_interrupt(void) { |
| 163 | return (smp_ctx(c: TOPC_NONE) & SMP_CTX_IRQ_MASK) != 0; |
| 164 | } |
| 165 | |
| 166 | static inline bool irq_not_in_interrupt(void) { |
| 167 | return (smp_ctx(c: TOPC_NONE) & SMP_CTX_IN_INTERRUPT_MASK) == 0; |
| 168 | } |
| 169 | |
| 170 | static inline bool irq_vector_is_exception(uint8_t vector) { |
| 171 | return vector < IRQ_EXCEPTION_COUNT; |
| 172 | } |
| 173 | |
| 174 | void ipi_send(uint32_t apic_id, uint8_t vector); |
| 175 | void nmi_send(uint32_t apic_id); |
| 176 | |
| 177 | void irq_set_alloc(int32_t entry, bool used); |
| 178 | int32_t irq_alloc_entry(void); |
| 179 | void irq_free_entry(int32_t entry); |
| 180 | bool irq_is_installed(int32_t entry); |
| 181 | void irq_free_entry(int32_t entry); |
| 182 | void irq_disable(irq_t irq); |
| 183 | void irq_enable(irq_t irq); |
| 184 | |