asm { INT_MP_CRASH_ADDR:: //Forward reference to work around compiler DU32 &IntMPCrash; INT_WAKE:: PUSH RDX PUSH RAX MOV EAX, &dev MOV EDX, U32 LAPIC_EOI MOV RAX, U64 CDevGlobals.uncached_alias[RAX] MOV U32 [RAX+RDX], 0 POP RAX POP RDX IRET IRQ_TIMER:: //I_TIMER CALL TASK_CONTEXT_SAVE CLD MOV RAX, U64 [RSP] MOV U64 CTask.rip[RSI], RAX MOV RAX, U64 16[RSP] MOV U64 CTask.rflags[RSI], RAX MOV RAX, U64 24[RSP] MOV U64 CTask.rsp[RSI], RAX XOR RAX, RAX MOV RDI, U64 GS:CCPU.addr[RAX] LOCK INC U64 CCPU.total_jiffies[RDI] BT U64 CTask.task_flags[RSI], TASKf_IDLE JNC @@05 LOCK INC U64 CCPU.idle_pt_hits[RDI] @@05: MOV RAX, U64 CCPU.profiler_timer_irq[RDI] TEST RAX, RAX JZ @@10 PUSH RSI CALL RAX //See ProfTimerInt(). JMP @@15 @@10: ADD RSP, 8 @@15: CLI MOV RAX, U64 CCPU.num[RDI] TEST RAX, RAX JZ @@20 MOV EAX, &dev MOV EDX, U32 LAPIC_EOI MOV RAX, U64 CDevGlobals.uncached_alias[RAX] MOV U32 [RAX + RDX], 0 JMP @@25 @@20: CALL &IntCore0TimerHandler //Only Core 0 calls this. @@25: XOR RAX, RAX CMP RSI, U64 GS:CCPU.idle_task[RAX] JE I32 RESTORE_EXECUTIVE_TASK_IF_READY JMP I32 RESTORE_RSI_TASK //************************************ INT_FAULT:: PUSH RBX PUSH RAX MOV BL, U8 16[RSP] //We pushed fault_num IntFaultHandlersNew(). XOR RAX, RAX MOV FS:U8 CTask.fault_num[RAX], BL POP RAX POP RBX ADD RSP, 8 //Pop fault_num CALL TASK_CONTEXT_SAVE XOR RDX, RDX MOV U64 CTask.fault_err_code[RSI], RDX MOV EDX, U32 CTask.fault_num[RSI] BT U64 [INT_FAULT_ERR_CODE_BITMAP], RDX JNC @@1 POP U64 CTask.fault_err_code[RSI] @@1: MOV RAX, U64 [RSP] MOV U64 CTask.rip[RSI], RAX MOV RAX, U64 16[RSP] MOV U64 CTask.rflags[RSI], RAX MOV RSP, U64 24[RSP] MOV U64 CTask.rsp[RSI], RSP MOV RBP, CTask.rbp[RSI] PUSH U64 CTask.fault_err_code[RSI] PUSH U64 CTask.fault_num[RSI] MOV RSI, CTask.rsi[RSI] CALL &Fault2 //See Fault2 JMP I32 RESTORE_FS_TASK INT_FAULT_ERR_CODE_BITMAP:: DU32 0x00027D00, 0, 0, 0, 0, 0, 0, 0; } U8 *IntEntryGet(I64 irq) {//Get interrupt handler. I64 handler_addr; CIDTEntry *entry = &dev.idt[irq]; handler_addr.u16[0] = entry->offset_low; handler_addr.u16[1] = entry->offset_mid; handler_addr.u32[1] = entry->offset_hi; return handler_addr; } U8 *IntEntrySet(I64 irq, U0 (*fp_new_handler)(), I64 type=IDTET_IRQ) {//Set interrupt handler. Returns old handler. See IDTET_IRQ. //See ::/Demo/Lectures/InterruptDemo.CC. //See ::/Demo/MultiCore/Interrupts.CC. I64 fp = fp_new_handler; U8 *old_handler; CIDTEntry *entry; PUSHFD CLI old_handler = IntEntryGet(irq); entry = &dev.idt[irq]; entry->seg_select = offset(CGDT.cs64); entry->offset_low = fp.u16[0]; entry->offset_mid = fp.u16[1]; entry->offset_hi = fp.u32[1]; entry->type_attr = 0x80 + type; //bit 7 is 'segment present' entry->ist = entry->zero = 0; //We don't care about the IST mechanism POPFD return old_handler; } U0 IntPICInit() {//Init 8259 OutU8(PIC_1, PIC_INIT); //IW (Initialization Word) 1 OutU8(PIC_2, PIC_INIT); //IW1 OutU8(PIC_1_DATA, 0x20); //IW2 Moving IRQ base from 0 -> 32 (beyond Intel reserved faults) OutU8(PIC_2_DATA, 0x28); //IW2 Moving IRQ base from 8 -> 40 OutU8(PIC_1_DATA, 0x04); //IW3 Telling PIC_1 PIC_2 exists. OutU8(PIC_2_DATA, 0x02); //IW3 Telling PIC_2 its cascade identity. OutU8(PIC_1_DATA, 0x0D); //IW4 8086 Mode, Buffered Mode (Master PIC) OutU8(PIC_2_DATA, 0x09); //IW4 8086 Mode, Buffered Mode (Slave PIC) OutU8(PIC_1_DATA, 0xFA); //Mask all but IRQ0 (timer) and IRQ2 Cascade. OutU8(PIC_2_DATA, 0xFF); } interrupt U0 IntNop() {//Make unplanned IRQs stop by all means! OutU8(PIC_2, PIC_EOI); OutU8(PIC_1, PIC_EOI); *(dev.uncached_alias + LAPIC_EOI)(U32 *) = 0; } interrupt U0 IntDivZero() { if (Gs->num) { mp_count = 1; debug.mp_crash->cpu_num = Gs->num; debug.mp_crash->task = Fs; MOV RAX, U64 8[RBP] //Get RIP off of stack. debug.mp_crash->rip = RAXGet; debug.mp_crash->message = "Div Zero"; debug.mp_crash->message_num = 0; MPInt(I_MP_CRASH, 0); SysHlt; } throw('DivZero'); } U8 *IntFaultHandlersNew() { I64 i; U8 *res = MAlloc(256 * 7, Fs->code_heap), *dst = res; for (i = 0; i < 256; i++) { *dst++ = 0x6A; //PUSH I8 xx *dst(I8 *)++ = i; *dst++ = 0xE9; //JMP I32 xxxxxxxx *dst(I32 *) = INT_FAULT - dst - 4; dst += 4; } return res; } U0 IntInit1() {//Interrupt descriptor table part1. I64 i; CSysLimitBase tmp_ptr; if (!Gs->num) //Gs is current CCPU struct { dev.idt = CAllocAligned(sizeof(CIDTEntry) * 256, 8); for (i = 0; i < 256; i++) IntEntrySet(i, &IntNop); } tmp_ptr.limit = 256 * sizeof(CIDTEntry) - 1; tmp_ptr.base = dev.idt; RAXSet(&tmp_ptr); LIDT U64 [RAX] } U0 IntInit2() {//Interrupt descriptor table part2: Core 0 Only. I64 i; PUSHFD CLI IntEntrySet(I_DIV_ZERO, &IntDivZero); for (i = 1; i < 32; i++) IntEntrySet(i, &debug.int_fault_code[7 * i]); /*In theory, we use the PIC mask reg to insure we don't get anything but keyboard, mouse and timer IRQs. In practice, I've gotten IRQ 0x27, perhaps because I didn't initialize the APIC. I go ahead and ACK PIC in IntNop(). I have no idea why I got a IRQ 0x27. */ IntEntrySet(I_NMI, _SYS_HLT); IntEntrySet(I_TIMER, IRQ_TIMER); IntEntrySet(I_MP_CRASH, *INT_MP_CRASH_ADDR(U32 *)); IntEntrySet(I_WAKE, INT_WAKE); IntEntrySet(I_DEBUG, &debug.int_fault_code[7 * I_DEBUG]); POPFD }