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.ZC. //See ::/Demo/MultiCore/Interrupts.ZC. 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; } I64 IntEntryAlloc() { // 'Allocate' a user irq num. // 64 user irqs available, 0xFF <--> 0xBF. // Goes backwards from 0xFF. I64 i, res = 0; for (i = 0xFF; i > 0xFF - 64; i--) if (!Bts(&dev.user_int_bitmap, i - 192)) { res = i; break; } return res; } 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; } I64 IntEntryFree(I64 irq) { // 'Free' a user irq num. Unsets bit in user irq bitmap. I64 res = 0; if (0xFF >= irq >= 0xFF - 64) if (res = Btr(&dev.user_int_bitmap, irq - 192)) IntEntrySet(irq, &IntNop); // clear the irq entry vect return res; } 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, Terry had gotten IRQ 0x27, he thought perhaps because he didn't initialize the APIC? We go ahead and ACK PIC in IntNop(). He had no idea why he 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 }