#include "interrupts.h" #include "kernel.h" #include "log.h" #include "e1000.h" #include "asm_int.h" #include "asm_pit.h" #include "driver/mouse.h" #include "driver/keyboard.h" #include "scheduler.h" #include "asm_x86.h" #include "smp.h" #include "apic.h" #include "ringbuffer.h" #include "compositor.h" #include "syscalls.h" /** The size of our interrupts table */ #define INT_MAX 256 // 0-255 /** Addresses of the registered interrupt handlers */ static uint32_t handlers[INT_MAX]; /** The interrupt descriptor table */ struct int_desc { uint16_t addrLo; uint16_t sel; uint8_t zeros; uint8_t flags; uint16_t addrHi; } idt[INT_MAX]; /** The interrupt descriptor table descriptor */ struct idt_desc { uint16_t size; uint16_t baseLo; uint16_t baseHi; } idtd; /** Sets a handler for a specific interrupt */ static void int_install_ir(int irq, uint16_t flags, uint16_t sel, void *addr) { uint64_t base=(uint32_t)&(*(uint32_t*)addr); idt[irq].addrLo = base & 0xffff; idt[irq].addrHi = (base >> 16) & 0xffff; idt[irq].zeros=0; idt[irq].flags=flags; idt[irq].sel=sel; } /** * Register an interrupt handler for a given irq number. * * Some general notes on interrupts * (TODO: consider moving this somewehre else) * * 1) In case of a syscall (coming from userspace) the handler should: * a) For a blocking call, set required params and call the scheduler! * b) Otherweise call x86_sti() by itself as soon as possiblle to * reenable interrupts. * * 2) In case of APIC Timer interrupt call the scheduler. * * 3) Keyboard, Mouse, E1000, PIT * just push your stuff somewhere for later processing as fast as * you can and reschedule to a kernel worker OR just iret. * * 4) IPI * we use them to force rescheduling only now * treated in the same way as a APIC Timer interrupt... * TODO: NOT CALL THIS IPI!! since we send it only on same cpu now :P * * NOTE: apic_eoi() should be called in all cases except the syscalls, * to signal the "end of interrupt" to the APIC. * * TODO: REMEMBER THAT WE WILL HAVE TO CHECK ALL OUR SYSCALL ARE REENTRANT! * ALSO IN MULTICPU SETTINGS! (check the userspace wrappers as well!) * EACH prog requires a sufficient kernel-stack as well! (check this!) * WE CAN GUARD with spinlocks / disabling interrupts when really needed.... * */ void interrupt_register(uint32_t irq, uint32_t func_addr) { if(irq<128||irq>160)kpanic("irq number out of range!"); if(handlers[irq]!=0)kpanic("handler already registered!"); handlers[irq]=func_addr; } /* * Interrupt dispatcher * Remeber that we are inside an interrupt here! */ uint32_t interrupt_handler(uint32_t esp, uint32_t irq) { uint32_t cpu=smp_get(SMP_APIC_ID); if(handlers[irq]!=0)// kb,mouse,e1000,pit,... { uint32_t (*f)(uint32_t esp)=handlers[irq]; esp=f(esp); if(irq!=INTERRUPT_SYSCALL)apic_eoi(); return esp; } if(irq==INTERRUPT_APIC_TIMER || irq==INTERRUPT_IPI) { if(cpu==0) // thi { // limit compositor to APIC_TIMER freq.(60hz?) if(irq==INTERRUPT_APIC_TIMER)compositor_wake(); } esp=scheduler_run(esp,-1); // just schedule to next task apic_eoi(); return esp; } if(irq==INTERRUPT_SYSCALL) { int pid=task_get_current_pid(); uint32_t *stack; stack=esp; task_set_esp(pid,esp); // this will get fucked if we get scheduled away... // extract variables from stack // (NOTE we pass them in some fucked up order, // they were pushed via pusha! uint32_t eax=stack[11]; uint32_t ebx=stack[8]; uint32_t ecx=stack[10]; uint32_t edx=stack[9]; // only chance to do somehting before we reenable interrupts! syscall_generic_prep(eax,edx,ecx,ebx,pid); // now we don't give a shit about gettting interrupted, yeah! // this is guaranteed to cause heavy troubles... since we are // not reentrant and do not guard anythin.. // keep brute force rescheduling till' we get through.... // TODO: implement some waiting queue and wake only if there // is any chance this will not fail again and again and again.. // TODO: get rid of this big KERNEL LOCK // it will also not work with SMP while(true) { int ok=syscall_generic_test(eax,edx,ecx,ebx,pid); if(ok)break; __asm__("int $0x81");// does NOT requie interrupts } // uff, once we got through we can do the syscall and get out // of this place... // x86_cli(); uint32_t ret=syscall_generic(eax,edx,ecx,ebx,pid); // x86_sti(); stack[12]=0x1; // indicate with 0x1 we WANT to set a return // value in ebx (0x0 will skip it) stack[13]=ret; // and THIS is our return value! #ifdef LOG_SYSCALLS klog("syscall ret=0x%08X",ret); #endif //__asm__("int $0x81"); return esp; // return to asm interrupt handler... and iret. } kpanic("unhandled interrupt %d",irq); } /** * init interrupt descriptor table (TODO; do we seriously have to hardcode this?) */ void interrupts_init() { klog("Initializing. IDT: 0x%08x, IDTD: 0x%08X",&idt,&idtd); // Default interrupt handling for(int i=0; i>16; idtd.baseLo=0xffff&addr; __asm__("lidt %0"::"m" (idtd)); }