PatchworkOS  966e257
A non-POSIX operating system.
Loading...
Searching...
No Matches
interrupt.c
Go to the documentation of this file.
2
3#include <kernel/cpu/cpu.h>
4#include <kernel/cpu/gdt.h>
5#include <kernel/cpu/irq.h>
8#include <kernel/log/log.h>
9#include <kernel/log/panic.h>
11#include <kernel/sched/thread.h>
12#include <kernel/sched/wait.h>
13
14#include <kernel/cpu/regs.h>
15
16#include <assert.h>
17
19{
20 ctx->inInterrupt = false;
21 ctx->oldRflags = 0;
22 ctx->disableDepth = 0;
23}
24
26{
27 uint64_t rflags = rflags_read();
28 asm volatile("cli" ::: "memory");
30 if (ctx->disableDepth == 0)
31 {
32 ctx->oldRflags = rflags;
33 }
34 ctx->disableDepth++;
35}
36
38{
40
42 assert(ctx->disableDepth != 0);
43 ctx->disableDepth--;
44
45 if (ctx->disableDepth == 0 && (ctx->oldRflags & RFLAGS_INTERRUPT_ENABLE))
46 {
47 asm volatile("sti" ::: "memory");
48 }
49}
50
51static void exception_handle_user(interrupt_frame_t* frame, const char* note)
52{
53 thread_t* thread = sched_thread_unsafe();
54 if (thread_send_note(thread, note) == ERR)
55 {
56 atomic_store(&thread->state, THREAD_DYING);
57 process_kill(thread->process, note);
58
59 cpu_t* self = cpu_get_unsafe();
60 sched_do(frame, self);
61 }
62}
63
65{
66 uintptr_t alignedFaultAddr = ROUND_DOWN(faultAddr, PAGE_SIZE);
67 if (stack_pointer_is_in_stack(stack, alignedFaultAddr, 1))
68 {
69 if (vmm_alloc(&thread->process->space, (void*)alignedFaultAddr, PAGE_SIZE, flags, VMM_ALLOC_FAIL_IF_MAPPED) ==
70 NULL)
71 {
72 if (errno == EEXIST) // Race condition, another CPU mapped the page.
73 {
74 return 0;
75 }
76 return ERR;
77 }
78 memset_s((void*)alignedFaultAddr, PAGE_SIZE, 0, PAGE_SIZE);
79
80 return 1;
81 }
82
83 return 0;
84}
85
87{
88 thread_t* thread = sched_thread_unsafe();
89 process_t* process = thread->process;
90 uintptr_t faultAddr = (uintptr_t)cr2_read();
91
92 if (frame->errorCode & PAGE_FAULT_PRESENT)
93 {
94 panic(frame, "page fault on present page at address 0x%llx", faultAddr);
95 }
96
97 uintptr_t alignedFaultAddr = ROUND_DOWN(faultAddr, PAGE_SIZE);
98 if (stack_pointer_overlaps_guard(&thread->kernelStack, alignedFaultAddr, 1))
99 {
100 panic(frame, "kernel stack overflow at address 0x%llx", faultAddr);
101 }
102
103 uint64_t result = exception_grow_stack(thread, faultAddr, &thread->kernelStack, PML_WRITE | PML_PRESENT);
104 if (result == ERR)
105 {
106 panic(frame, "failed to grow kernel stack for page fault at address 0x%llx", faultAddr);
107 }
108
109 if (result == 1)
110 {
111 return;
112 }
113
114 result = exception_grow_stack(thread, faultAddr, &thread->userStack, PML_USER | PML_WRITE | PML_PRESENT);
115 if (result == ERR)
116 {
117 panic(frame, "failed to grow user stack for page fault at address 0x%llx", faultAddr);
118 }
119
120 if (result == 1)
121 {
122 return;
123 }
124
125 panic(frame, "invalid page fault at address 0x%llx", faultAddr);
126}
127
129{
130 thread_t* thread = sched_thread_unsafe();
131 process_t* process = thread->process;
132 uintptr_t faultAddr = (uintptr_t)cr2_read();
133
134 if (frame->errorCode & PAGE_FAULT_PRESENT)
135 {
137 F("pagefault at 0x%llx when %s present page at 0x%llx", frame->rip,
138 (frame->errorCode & PAGE_FAULT_WRITE) ? "writing to" : "reading from", faultAddr));
139 return;
140 }
141
142 uintptr_t alignedFaultAddr = ROUND_DOWN(faultAddr, PAGE_SIZE);
143 if (stack_pointer_overlaps_guard(&thread->userStack, alignedFaultAddr, 1))
144 {
145 exception_handle_user(frame, F("pagefault at 0x%llx due to stack overflow at 0x%llx", frame->rip, faultAddr));
146 return;
147 }
148
149 uint64_t result = exception_grow_stack(thread, faultAddr, &thread->userStack, PML_USER | PML_WRITE | PML_PRESENT);
150 if (result == ERR)
151 {
152 exception_handle_user(frame, F("pagefault at 0x%llx failed to grow stack at 0x%llx", frame->rip, faultAddr));
153 return;
154 }
155
156 if (result == 1)
157 {
158 return;
159 }
160
162 F("pagefault at 0x%llx when %s 0x%llx", frame->rip,
163 (frame->errorCode & PAGE_FAULT_WRITE) ? "writing" : "reading", faultAddr));
164}
165
167{
168 switch (frame->vector)
169 {
172 {
173 panic(frame, "divide by zero");
174 }
175 exception_handle_user(frame, F("divbyzero at 0x%llx", frame->rip));
176 return;
179 {
180 panic(frame, "invalid opcode");
181 }
182 exception_handle_user(frame, F("illegal instruction at 0x%llx", frame->rip));
183 return;
185 panic(frame, "double fault");
186 return;
187 case VECTOR_NMI:
188 return; /// @todo Handle NMIs properly.
191 {
193 return;
194 }
196 return;
197 default:
198 panic(frame, "unhandled exception vector 0x%x", frame->vector);
199 }
200}
201
203{
204 if (frame->vector < VECTOR_EXCEPTION_END) // Avoid extra stuff for exceptions
205 {
206 exception_handler(frame);
207 return;
208 }
209
210 if (frame->vector == VECTOR_SPURIOUS)
211 {
212 return;
213 }
214
215 cpu_t* self = cpu_get_unsafe();
216 assert(self != NULL);
217
218 self->interrupt.inInterrupt = true;
220
221 if (frame->vector >= VECTOR_EXTERNAL_START && frame->vector < VECTOR_EXTERNAL_END)
222 {
223 irq_dispatch(frame, self);
224 }
225 else if (frame->vector == VECTOR_FAKE)
226 {
227 // Do nothing.
228 }
229 else if (frame->vector == VECTOR_TIMER)
230 {
231 timer_ack_eoi(frame, self);
232 }
233 else if (frame->vector == VECTOR_IPI)
234 {
235 ipi_handle_pending(frame, self);
236 }
237 else
238 {
239 panic(NULL, "Invalid interrupt vector 0x%x", frame->vector);
240 }
241
242 note_handle_pending(frame, self);
243 wait_check_timeouts(frame, self);
244 sched_do(frame, self);
245
247
248 perf_interrupt_end(self);
249 self->interrupt.inInterrupt = false;
250
251 // This is a sanity check to make sure blocking and scheduling is functioning correctly.
253}
#define assert(expression)
Definition assert.h:29
void interrupt_ctx_init(interrupt_ctx_t *ctx)
Initializes the interrupt context.
Definition interrupt.c:18
void interrupt_handler(interrupt_frame_t *frame)
Handles CPU interrupts.
Definition interrupt.c:202
#define INTERRUPT_FRAME_IN_USER_SPACE(frame)
Checks if a interrupt frame is from user space.
Definition interrupt.h:180
void interrupt_disable(void)
Disable interrupts and increment the disableDepth.
Definition interrupt.c:25
void interrupt_enable(void)
Decrement the CLI depth and enable interrupts if depth reaches zero and interrupts were previously en...
Definition interrupt.c:37
@ PAGE_FAULT_WRITE
Definition interrupt.h:59
@ PAGE_FAULT_PRESENT
Definition interrupt.h:58
@ VECTOR_EXTERNAL_END
Exclusive end of external interrupts.
Definition interrupt.h:124
@ VECTOR_EXTERNAL_START
Inclusive start of external interrupts (handled by the IRQ system).
Definition interrupt.h:123
@ VECTOR_IPI
See IPI for more information.
Definition interrupt.h:130
@ VECTOR_PAGE_FAULT
Definition interrupt.h:110
@ VECTOR_DOUBLE_FAULT
Definition interrupt.h:104
@ VECTOR_FAKE
Used to implement interrupt_fake().
Definition interrupt.h:129
@ VECTOR_NMI
Definition interrupt.h:98
@ VECTOR_TIMER
See Timer subsystem for more information.
Definition interrupt.h:131
@ VECTOR_DIVIDE_ERROR
Definition interrupt.h:96
@ VECTOR_SPURIOUS
Made available for any component to use as a sink for spurious interrupts.
Definition interrupt.h:132
@ VECTOR_INVALID_OPCODE
Definition interrupt.h:102
@ VECTOR_EXCEPTION_END
Exclusive end of exceptions.
Definition interrupt.h:121
void ipi_handle_pending(interrupt_frame_t *frame, cpu_t *self)
Handle pending IPIs on the current CPU.
Definition ipi.c:23
void irq_dispatch(interrupt_frame_t *frame, cpu_t *self)
Dispatch an IRQ.
Definition irq.c:127
bool stack_pointer_overlaps_guard(stack_pointer_t *stack, uintptr_t addr, uint64_t length)
Check if an region overlaps the guard.
bool stack_pointer_is_in_stack(stack_pointer_t *stack, uintptr_t addr, uint64_t length)
Check if an region is within the stack.
void cpu_stacks_overflow_check(cpu_t *cpu)
Checks for CPU stack overflows.
Definition cpu.c:193
static cpu_t * cpu_get_unsafe(void)
Gets the current CPU structure without disabling interrupts.
Definition cpu.h:299
void perf_interrupt_begin(cpu_t *self)
Called at the beginning of an interrupt to update cpu performance data.
Definition perf.c:156
void perf_interrupt_end(cpu_t *self)
Called at the end of an interrupt to update cpu performance data.
Definition perf.c:192
bool note_handle_pending(interrupt_frame_t *frame, cpu_t *self)
Handle pending notes for the current thread.
Definition note.c:86
NORETURN void panic(const interrupt_frame_t *frame, const char *format,...)
Panic the kernel, printing a message and halting.
Definition panic.c:266
@ PML_USER
@ PML_PRESENT
@ PML_WRITE
void * vmm_alloc(space_t *space, void *virtAddr, uint64_t length, pml_flags_t pmlFlags, vmm_alloc_flags_t allocFlags)
Allocates and maps virtual memory in a given address space.
Definition vmm.c:163
@ VMM_ALLOC_FAIL_IF_MAPPED
If set and any page is already mapped, fail and set errno to EEXIST.
Definition vmm.h:124
void process_kill(process_t *process, const char *status)
Kills a process, pushing it to the reaper.
Definition process.c:826
uint64_t thread_send_note(thread_t *thread, const char *string)
Send a note to a thread.
Definition thread.c:156
@ THREAD_DYING
The thread is currently dying, it will be freed by the scheduler once its invoked.
Definition thread.h:36
void wait_check_timeouts(interrupt_frame_t *frame, cpu_t *self)
Check for timeouts and unblock threads as needed.
Definition wait.c:69
thread_t * sched_thread_unsafe(void)
Retrieves the currently running thread without disabling interrupts.
Definition sched.c:626
void sched_do(interrupt_frame_t *frame, cpu_t *self)
Perform a scheduling operation.
Definition sched.c:521
void timer_ack_eoi(interrupt_frame_t *frame, cpu_t *self)
Acknowledge a timer interrupt and send EOI.
Definition timer.c:28
#define EEXIST
File exists.
Definition errno.h:117
#define errno
Error number variable.
Definition errno.h:27
#define F(format,...)
Format string macro.
Definition io.h:83
#define ROUND_DOWN(number, multiple)
Definition math.h:21
#define PAGE_SIZE
The size of a memory page in bytes.
Definition proc.h:106
#define NULL
Pointer error value.
Definition NULL.h:23
#define ERR
Integer error value.
Definition ERR.h:17
static void exception_handler(interrupt_frame_t *frame)
Definition interrupt.c:166
static void exception_user_page_fault_handler(interrupt_frame_t *frame)
Definition interrupt.c:128
static uint64_t exception_grow_stack(thread_t *thread, uintptr_t faultAddr, stack_pointer_t *stack, pml_flags_t flags)
Definition interrupt.c:64
static void exception_kernel_page_fault_handler(interrupt_frame_t *frame)
Definition interrupt.c:86
static void exception_handle_user(interrupt_frame_t *frame, const char *note)
Definition interrupt.c:51
errno_t memset_s(void *s, rsize_t smax, int c, rsize_t n)
Definition memset_s.c:9
static const path_flag_t flags[]
Definition path.c:42
static pmm_stack_t stack
Definition pmm.c:38
#define RFLAGS_INTERRUPT_ENABLE
Definition regs.h:32
static uint64_t cr2_read()
Definition regs.h:114
static uint64_t rflags_read()
Definition regs.h:78
#define atomic_store(object, desired)
Definition stdatomic.h:289
__UINT64_TYPE__ uint64_t
Definition stdint.h:17
__UINTPTR_TYPE__ uintptr_t
Definition stdint.h:43
CPU structure.
Definition cpu.h:122
interrupt_ctx_t interrupt
Definition cpu.h:130
Per-CPU Interrupt Context.
Definition interrupt.h:189
uint32_t disableDepth
Definition interrupt.h:192
uint64_t oldRflags
Definition interrupt.h:191
Trap Frame Structure.
Definition interrupt.h:146
uint64_t errorCode
Definition interrupt.h:164
A entry in a page table without a specified address or callback ID.
Process structure.
Definition process.h:205
space_t space
Definition process.h:210
Structure to define a stack in memory.
Thread of execution structure.
Definition thread.h:56
process_t * process
The parent process that the thread executes within.
Definition thread.h:57
stack_pointer_t kernelStack
The kernel stack of the thread.
Definition thread.h:68
stack_pointer_t userStack
The user stack of the thread.
Definition thread.h:69