#include #include #include #include #include #include #include #include enum { Nmi = 0x02, GpFault = 0x0d, PageFault = 0x0e, }; typedef struct { uint64_t ip, cs, flags; /* only valid if switching from/into user mode */ uint64_t sp, ss; } IretFrame; typedef struct IsrFrame { UserRegs regs; /* doesn't include ip, flags, sp */ uint64_t _retaddr; uint64_t errcode; IretFrame iret; } __attribute__((packed)) IsrFrame; void (*irq_fn[16])(void) = {0}; static uint64_t getcr2(void) { uint64_t cr2; asm("mov %%cr2, %0" : "=r"(cr2)); return cr2; } static void log_interrupt(uint8_t inr, IsrFrame *sf) { IretFrame *iret = &sf->iret; kprintf("interrupt %d, rip = k/%08x, cs 0x%x, code 0x%x\n", inr, iret->ip, iret->cs, sf->errcode); if ((iret->cs & 0x3) == 0) { /* ring 0? */ uint64_t *stack = (void*)iret->sp; kprintf("kernel rsp = %p, *rsp = %p\n", stack, *stack); } if (inr == PageFault) { kprintf("addr 0x%x\n", getcr2()); } } void isr_stage3(uint8_t inr, IsrFrame *sf) { IretFrame *iret = &sf->iret; uint8_t irqn = inr - IRQ_IBASE; if (irqn < 16) { if (irq_fn[irqn]) { irq_fn[irqn](); irq_eoi(irqn); return; } } if (inr == Nmi) { /* print some debugging information */ log_interrupt(inr, sf); mem_debugprint(); return; } if ((iret->cs & 0x3) == 0) { /* ring 0? */ log_interrupt(inr, sf); cpu_halt(); } else { /* in user */ if (inr == PageFault) { char buf[64]; int len = snprintf( buf, sizeof(buf), "sys: fault %s ip=%p cr2=%p", /* format inspired by plan9 */ (sf->errcode & 2) ? "write" : "read", sf->iret.ip, getcr2() ); /* save registers (ignoring SSE) */ proc_cur->regs = sf->regs; proc_cur->regs.rsp = sf->iret.sp; proc_cur->regs.rip = sf->iret.ip; proc_cur->regs.flags = sf->iret.flags; proc_intr(proc_cur, buf, len); proc_intr_deliver(proc_cur); /* restore */ sf->regs = proc_cur->regs; sf->iret.sp = proc_cur->regs.rsp; sf->iret.ip = proc_cur->regs.rip; sf->iret.flags = proc_cur->regs.flags; return; } proc_kill(proc_cur, inr); proc_switch_any(); } }