/* * interrupt.c * * Copyright (C) 2018 Aleksandar Andrejevic * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #include #include #include #include #include #include static byte_t isr_stubs[IDT_NUM_INTERRUPTS * ISR_STUB_SIZE]; static idt_entry_t idt[IDT_NUM_INTERRUPTS]; static interrupt_handler_t handlers[IDT_NUM_INTERRUPTS]; static void idt_main_handler(byte_t interrupt_num, registers_t regs) { regs.esp += 16; if (SEGMENT_RPL(regs.cs) != 0) regs.esp += 8; if (handlers[interrupt_num].procedure == NULL) return; thread_t *thread = get_current_thread(); if (thread) { if (thread->in_kernel == 0) thread->last_context = ®s; thread->in_kernel++; } if (handlers[interrupt_num].interrupts) cpu_enable_interrupts(); handlers[interrupt_num].procedure(®s, interrupt_num); if (thread) { ASSERT(thread->in_kernel > 0); if (--thread->in_kernel == 0) { thread->last_context = NULL; if (thread->terminating) thread->terminated = TRUE; if (thread->terminated || thread->frozen > 0) syscall_yield_quantum(); ASSERT(!thread->terminated && (thread->frozen <= 0)); } } cpu_disable_interrupts(); } dword_t set_int_handler(byte_t interrupt_num, isr_proc_t proc, bool_t interrupts, bool_t usermode) { dword_t ret = ERR_SUCCESS; critical_t critical; enter_critical(&critical); if (handlers[interrupt_num].procedure != NULL) { ret = ERR_EXISTS; goto done; } handlers[interrupt_num].procedure = proc; handlers[interrupt_num].interrupts = interrupts; idt[interrupt_num].type = usermode ? IDT_GATE_USER : IDT_GATE_KERNEL; done: leave_critical(&critical); return ret; } dword_t remove_int_handler(byte_t interrupt_num) { dword_t ret = ERR_SUCCESS; critical_t critical; enter_critical(&critical); if (handlers[interrupt_num].procedure == NULL) { ret = ERR_NOTFOUND; goto done; } handlers[interrupt_num].procedure = NULL; idt[interrupt_num].type = IDT_GATE_KERNEL; done: leave_critical(&critical); return ret; } void interrupt_init(void) { dword_t i, offset = 0; for (i = 0; i < IDT_NUM_INTERRUPTS; i++) { idt[i].offset = (dword_t)&isr_stubs[offset] & 0xFFFF; idt[i].offset_high = (dword_t)&isr_stubs[offset] >> 16; idt[i].selector = get_kernel_code_selector(); idt[i].type = IDT_GATE_KERNEL; idt[i].zero = 0; if (!HAS_ERROR_CODE(i)) { isr_stubs[offset++] = 0x6A; // push 0 isr_stubs[offset++] = 0x00; } isr_stubs[offset++] = 0x60; // pushad isr_stubs[offset++] = 0x1E; // push ds isr_stubs[offset++] = 0x6A; // push get_kernel_data_selector() isr_stubs[offset++] = get_kernel_data_selector(); isr_stubs[offset++] = 0x58; // pop eax isr_stubs[offset++] = 0x8E; // mov ds, ax isr_stubs[offset++] = 0xD8; isr_stubs[offset++] = 0x8E; // mov es, ax isr_stubs[offset++] = 0xC0; isr_stubs[offset++] = 0x8E; // mov fs, ax isr_stubs[offset++] = 0xE0; isr_stubs[offset++] = 0x8E; // mov gs, ax isr_stubs[offset++] = 0xE8; isr_stubs[offset++] = 0x6A; // push i isr_stubs[offset++] = (byte_t) i; isr_stubs[offset++] = 0xE8; // call idt_main_handler dword_t rel_addr = (dword_t)idt_main_handler - (dword_t)&isr_stubs[offset + 4]; isr_stubs[offset++] = rel_addr & 0xFF; isr_stubs[offset++] = (rel_addr >> 8) & 0xFF; isr_stubs[offset++] = (rel_addr >> 16) & 0xFF; isr_stubs[offset++] = (rel_addr >> 24) & 0xFF; isr_stubs[offset++] = 0x58; // pop eax if (!HAS_ERROR_CODE(i)) { isr_stubs[offset++] = 0x81; // cmp dword [esp + 0x24], CONTEXT_SWITCH_MAGIC isr_stubs[offset++] = 0x7C; isr_stubs[offset++] = 0x24; isr_stubs[offset++] = 0x24; isr_stubs[offset++] = CONTEXT_SWITCH_MAGIC & 0xFF; isr_stubs[offset++] = (CONTEXT_SWITCH_MAGIC >> 8) & 0xFF; isr_stubs[offset++] = (CONTEXT_SWITCH_MAGIC >> 16) & 0xFF; isr_stubs[offset++] = CONTEXT_SWITCH_MAGIC >> 24; isr_stubs[offset++] = 0x75; // jnz +0x04 isr_stubs[offset++] = 0x04; isr_stubs[offset++] = 0x8B; // mov esp, [esp + 0x10] isr_stubs[offset++] = 0x64; isr_stubs[offset++] = 0x24; isr_stubs[offset++] = 0x10; } isr_stubs[offset++] = 0x58; // pop eax isr_stubs[offset++] = 0x8E; // mov ds, ax isr_stubs[offset++] = 0xD8; isr_stubs[offset++] = 0x8E; // mov es, ax isr_stubs[offset++] = 0xC0; isr_stubs[offset++] = 0x8E; // mov fs, ax isr_stubs[offset++] = 0xE0; isr_stubs[offset++] = 0x8E; // mov gs, ax isr_stubs[offset++] = 0xE8; isr_stubs[offset++] = 0x61; // popad isr_stubs[offset++] = 0x83; // add esp, 4 isr_stubs[offset++] = 0xC4; isr_stubs[offset++] = 0x04; isr_stubs[offset++] = 0xCF; // iret } cpu_set_interrupt_table(idt, sizeof(idt)); }