Index: head/sys/i386/i386/apic_vector.s =================================================================== --- head/sys/i386/i386/apic_vector.s (revision 153145) +++ head/sys/i386/i386/apic_vector.s (revision 153146) @@ -1,313 +1,309 @@ /*- * Copyright (c) 1989, 1990 William F. Jolitz. * Copyright (c) 1990 The Regents of the University of California. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * from: vector.s, 386BSD 0.1 unknown origin * $FreeBSD$ */ /* * Interrupt entry points for external interrupts triggered by I/O APICs * as well as IPI handlers. */ #include "opt_smp.h" #include #include #include "assym.s" /* * I/O Interrupt Entry Point. Rather than having one entry point for * each interrupt source, we use one entry point for each 32-bit word * in the ISR. The handler determines the highest bit set in the ISR, * translates that into a vector, and passes the vector to the * lapic_handle_intr() function. */ #define ISR_VEC(index, vec_name) \ .text ; \ SUPERALIGN_TEXT ; \ IDTVEC(vec_name) ; \ PUSH_FRAME ; \ SET_KERNEL_SREGS ; \ FAKE_MCOUNT(TF_EIP(%esp)) ; \ movl lapic, %edx ; /* pointer to local APIC */ \ movl LA_ISR + 16 * (index)(%edx), %eax ; /* load ISR */ \ bsrl %eax, %eax ; /* index of highset set bit in ISR */ \ jz 2f ; \ addl $(32 * index),%eax ; \ 1: ; \ pushl %eax ; /* pass the IRQ */ \ call lapic_handle_intr ; \ addl $4, %esp ; /* discard parameter */ \ MEXITCOUNT ; \ jmp doreti ; \ 2: movl $-1, %eax ; /* send a vector of -1 */ \ jmp 1b /* * Handle "spurious INTerrupts". * Notes: * This is different than the "spurious INTerrupt" generated by an * 8259 PIC for missing INTs. See the APIC documentation for details. * This routine should NOT do an 'EOI' cycle. */ .text SUPERALIGN_TEXT IDTVEC(spuriousint) /* No EOI cycle used here */ iret ISR_VEC(1, apic_isr1) ISR_VEC(2, apic_isr2) ISR_VEC(3, apic_isr3) ISR_VEC(4, apic_isr4) ISR_VEC(5, apic_isr5) ISR_VEC(6, apic_isr6) ISR_VEC(7, apic_isr7) /* * Local APIC periodic timer handler. */ .text SUPERALIGN_TEXT IDTVEC(timerint) PUSH_FRAME SET_KERNEL_SREGS FAKE_MCOUNT(TF_EIP(%esp)) - pushl $0 /* XXX convert trapframe to clockframe */ call lapic_handle_timer - addl $4, %esp /* XXX convert clockframe to trapframe */ MEXITCOUNT jmp doreti #ifdef SMP /* * Global address space TLB shootdown. */ .text SUPERALIGN_TEXT IDTVEC(invltlb) pushl %eax pushl %ds movl $KDSEL, %eax /* Kernel data selector */ movl %eax, %ds #if defined(COUNT_XINVLTLB_HITS) || defined(COUNT_IPIS) pushl %fs movl $KPSEL, %eax /* Private space selector */ movl %eax, %fs movl PCPU(CPUID), %eax popl %fs #ifdef COUNT_XINVLTLB_HITS incl xhits_gbl(,%eax,4) #endif #ifdef COUNT_IPIS movl ipi_invltlb_counts(,%eax,4),%eax incl (%eax) #endif #endif movl %cr3, %eax /* invalidate the TLB */ movl %eax, %cr3 movl lapic, %eax movl $0, LA_EOI(%eax) /* End Of Interrupt to APIC */ lock incl smp_tlb_wait popl %ds popl %eax iret /* * Single page TLB shootdown */ .text SUPERALIGN_TEXT IDTVEC(invlpg) pushl %eax pushl %ds movl $KDSEL, %eax /* Kernel data selector */ movl %eax, %ds #if defined(COUNT_XINVLTLB_HITS) || defined(COUNT_IPIS) pushl %fs movl $KPSEL, %eax /* Private space selector */ movl %eax, %fs movl PCPU(CPUID), %eax popl %fs #ifdef COUNT_XINVLTLB_HITS incl xhits_pg(,%eax,4) #endif #ifdef COUNT_IPIS movl ipi_invlpg_counts(,%eax,4),%eax incl (%eax) #endif #endif movl smp_tlb_addr1, %eax invlpg (%eax) /* invalidate single page */ movl lapic, %eax movl $0, LA_EOI(%eax) /* End Of Interrupt to APIC */ lock incl smp_tlb_wait popl %ds popl %eax iret /* * Page range TLB shootdown. */ .text SUPERALIGN_TEXT IDTVEC(invlrng) pushl %eax pushl %edx pushl %ds movl $KDSEL, %eax /* Kernel data selector */ movl %eax, %ds #if defined(COUNT_XINVLTLB_HITS) || defined(COUNT_IPIS) pushl %fs movl $KPSEL, %eax /* Private space selector */ movl %eax, %fs movl PCPU(CPUID), %eax popl %fs #ifdef COUNT_XINVLTLB_HITS incl xhits_rng(,%eax,4) #endif #ifdef COUNT_IPIS movl ipi_invlrng_counts(,%eax,4),%eax incl (%eax) #endif #endif movl smp_tlb_addr1, %edx movl smp_tlb_addr2, %eax 1: invlpg (%edx) /* invalidate single page */ addl $PAGE_SIZE, %edx cmpl %eax, %edx jb 1b movl lapic, %eax movl $0, LA_EOI(%eax) /* End Of Interrupt to APIC */ lock incl smp_tlb_wait popl %ds popl %edx popl %eax iret /* * Forward hardclock to another CPU. Pushes a clockframe and calls * forwarded_hardclock(). */ .text SUPERALIGN_TEXT IDTVEC(ipi_intr_bitmap_handler) PUSH_FRAME SET_KERNEL_SREGS movl lapic, %edx movl $0, LA_EOI(%edx) /* End Of Interrupt to APIC */ FAKE_MCOUNT(TF_EIP(%esp)) - pushl $0 /* XXX convert trapframe to clockframe */ call ipi_bitmap_handler - addl $4, %esp /* XXX convert clockframe to trapframe */ MEXITCOUNT jmp doreti /* * Executed by a CPU when it receives an IPI_STOP from another CPU. */ .text SUPERALIGN_TEXT IDTVEC(cpustop) PUSH_FRAME SET_KERNEL_SREGS movl lapic, %eax movl $0, LA_EOI(%eax) /* End Of Interrupt to APIC */ call cpustop_handler POP_FRAME iret /* * Executed by a CPU when it receives a RENDEZVOUS IPI from another CPU. * * - Calls the generic rendezvous action function. */ .text SUPERALIGN_TEXT IDTVEC(rendezvous) PUSH_FRAME SET_KERNEL_SREGS #ifdef COUNT_IPIS movl PCPU(CPUID), %eax movl ipi_rendezvous_counts(,%eax,4), %eax incl (%eax) #endif call smp_rendezvous_action movl lapic, %eax movl $0, LA_EOI(%eax) /* End Of Interrupt to APIC */ POP_FRAME iret /* * Clean up when we lose out on the lazy context switch optimization. * ie: when we are about to release a PTD but a cpu is still borrowing it. */ SUPERALIGN_TEXT IDTVEC(lazypmap) PUSH_FRAME SET_KERNEL_SREGS call pmap_lazyfix_action movl lapic, %eax movl $0, LA_EOI(%eax) /* End Of Interrupt to APIC */ POP_FRAME iret #endif /* SMP */ Index: head/sys/i386/i386/db_trace.c =================================================================== --- head/sys/i386/i386/db_trace.c (revision 153145) +++ head/sys/i386/i386/db_trace.c (revision 153146) @@ -1,707 +1,716 @@ /*- * Mach Operating System * Copyright (c) 1991,1990 Carnegie Mellon University * All Rights Reserved. * * Permission to use, copy, modify and distribute this software and its * documentation is hereby granted, provided that both the copyright * notice and this permission notice appear in all copies of the * software, derivative works or modified versions, and any portions * thereof, and that both notices appear in supporting documentation. * * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. * * Carnegie Mellon requests users of this software to return to * * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU * School of Computer Science * Carnegie Mellon University * Pittsburgh PA 15213-3890 * * any improvements or extensions that they make and grant Carnegie the * rights to redistribute these changes. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static db_varfcn_t db_dr0; static db_varfcn_t db_dr1; static db_varfcn_t db_dr2; static db_varfcn_t db_dr3; static db_varfcn_t db_dr4; static db_varfcn_t db_dr5; static db_varfcn_t db_dr6; static db_varfcn_t db_dr7; static db_varfcn_t db_esp; static db_varfcn_t db_frame; static db_varfcn_t db_ss; /* * Machine register set. */ #define DB_OFFSET(x) (db_expr_t *)offsetof(struct trapframe, x) struct db_variable db_regs[] = { { "cs", DB_OFFSET(tf_cs), db_frame }, { "ds", DB_OFFSET(tf_ds), db_frame }, { "es", DB_OFFSET(tf_es), db_frame }, { "fs", DB_OFFSET(tf_fs), db_frame }, { "ss", NULL, db_ss }, { "eax", DB_OFFSET(tf_eax), db_frame }, { "ecx", DB_OFFSET(tf_ecx), db_frame }, { "edx", DB_OFFSET(tf_edx), db_frame }, { "ebx", DB_OFFSET(tf_ebx), db_frame }, { "esp", NULL, db_esp }, { "ebp", DB_OFFSET(tf_ebp), db_frame }, { "esi", DB_OFFSET(tf_esi), db_frame }, { "edi", DB_OFFSET(tf_edi), db_frame }, { "eip", DB_OFFSET(tf_eip), db_frame }, { "efl", DB_OFFSET(tf_eflags), db_frame }, { "dr0", NULL, db_dr0 }, { "dr1", NULL, db_dr1 }, { "dr2", NULL, db_dr2 }, { "dr3", NULL, db_dr3 }, { "dr4", NULL, db_dr4 }, { "dr5", NULL, db_dr5 }, { "dr6", NULL, db_dr6 }, { "dr7", NULL, db_dr7 }, }; struct db_variable *db_eregs = db_regs + sizeof(db_regs)/sizeof(db_regs[0]); #define DB_DRX_FUNC(reg) \ static int \ db_ ## reg (vp, valuep, op) \ struct db_variable *vp; \ db_expr_t * valuep; \ int op; \ { \ if (op == DB_VAR_GET) \ *valuep = r ## reg (); \ else \ load_ ## reg (*valuep); \ return (1); \ } DB_DRX_FUNC(dr0) DB_DRX_FUNC(dr1) DB_DRX_FUNC(dr2) DB_DRX_FUNC(dr3) DB_DRX_FUNC(dr4) DB_DRX_FUNC(dr5) DB_DRX_FUNC(dr6) DB_DRX_FUNC(dr7) static __inline int get_esp(struct trapframe *tf) { return ((ISPL(tf->tf_cs)) ? tf->tf_esp : (db_expr_t)tf + (uintptr_t)DB_OFFSET(tf_esp)); } static int db_frame(struct db_variable *vp, db_expr_t *valuep, int op) { int *reg; if (kdb_frame == NULL) return (0); reg = (int *)((uintptr_t)kdb_frame + (db_expr_t)vp->valuep); if (op == DB_VAR_GET) *valuep = *reg; else *reg = *valuep; return (1); } static int db_esp(struct db_variable *vp, db_expr_t *valuep, int op) { if (kdb_frame == NULL) return (0); if (op == DB_VAR_GET) *valuep = get_esp(kdb_frame); else if (ISPL(kdb_frame->tf_cs)) kdb_frame->tf_esp = *valuep; return (1); } static int db_ss(struct db_variable *vp, db_expr_t *valuep, int op) { if (kdb_frame == NULL) return (0); if (op == DB_VAR_GET) *valuep = (ISPL(kdb_frame->tf_cs)) ? kdb_frame->tf_ss : rss(); else if (ISPL(kdb_frame->tf_cs)) kdb_frame->tf_ss = *valuep; return (1); } /* * Stack trace. */ #define INKERNEL(va) (((vm_offset_t)(va)) >= USRSTACK) struct i386_frame { struct i386_frame *f_frame; int f_retaddr; int f_arg0; }; #define NORMAL 0 #define TRAP 1 #define INTERRUPT 2 #define SYSCALL 3 #define DOUBLE_FAULT 4 +#define TRAP_INTERRUPT 5 static void db_nextframe(struct i386_frame **, db_addr_t *, struct thread *); static int db_numargs(struct i386_frame *); static void db_print_stack_entry(const char *, int, char **, int *, db_addr_t); static void decode_syscall(int, struct thread *); static char * watchtype_str(int type); int i386_set_watch(int watchnum, unsigned int watchaddr, int size, int access, struct dbreg * d); int i386_clr_watch(int watchnum, struct dbreg * d); /* * Figure out how many arguments were passed into the frame at "fp". */ static int db_numargs(fp) struct i386_frame *fp; { int *argp; int inst; int args; argp = (int *)db_get_value((int)&fp->f_retaddr, 4, FALSE); /* * XXX etext is wrong for LKMs. We should attempt to interpret * the instruction at the return address in all cases. This * may require better fault handling. */ if (argp < (int *)btext || argp >= (int *)etext) { args = 5; } else { inst = db_get_value((int)argp, 4, FALSE); if ((inst & 0xff) == 0x59) /* popl %ecx */ args = 1; else if ((inst & 0xffff) == 0xc483) /* addl $Ibs, %esp */ args = ((inst >> 16) & 0xff) / 4; else args = 5; } return (args); } static void db_print_stack_entry(name, narg, argnp, argp, callpc) const char *name; int narg; char **argnp; int *argp; db_addr_t callpc; { db_printf("%s(", name); while (narg) { if (argnp) db_printf("%s=", *argnp++); db_printf("%r", db_get_value((int)argp, 4, FALSE)); argp++; if (--narg != 0) db_printf(","); } db_printf(") at "); db_printsym(callpc, DB_STGY_PROC); db_printf("\n"); } static void decode_syscall(int number, struct thread *td) { struct proc *p; c_db_sym_t sym; db_expr_t diff; sy_call_t *f; const char *symname; db_printf(" (%d", number); p = (td != NULL) ? td->td_proc : NULL; if (p != NULL && 0 <= number && number < p->p_sysent->sv_size) { f = p->p_sysent->sv_table[number].sy_call; sym = db_search_symbol((db_addr_t)f, DB_STGY_ANY, &diff); if (sym != DB_SYM_NULL && diff == 0) { db_symbol_values(sym, &symname, NULL); db_printf(", %s, %s", p->p_sysent->sv_name, symname); } } db_printf(")"); } /* * Figure out the next frame up in the call stack. */ static void db_nextframe(struct i386_frame **fp, db_addr_t *ip, struct thread *td) { struct trapframe *tf; int frame_type; int eip, esp, ebp; db_expr_t offset; c_db_sym_t sym; const char *name; eip = db_get_value((int) &(*fp)->f_retaddr, 4, FALSE); ebp = db_get_value((int) &(*fp)->f_frame, 4, FALSE); /* * Figure out frame type. We look at the address just before * the saved instruction pointer as the saved EIP is after the * call function, and if the function being called is marked as * dead (such as panic() at the end of dblfault_handler()), then * the instruction at the saved EIP will be part of a different * function (syscall() in this example) rather than the one that * actually made the call. */ frame_type = NORMAL; sym = db_search_symbol(eip - 1, DB_STGY_ANY, &offset); db_symbol_values(sym, &name, NULL); if (name != NULL) { if (strcmp(name, "calltrap") == 0 || strcmp(name, "fork_trampoline") == 0) frame_type = TRAP; else if (strncmp(name, "Xatpic_intr", 11) == 0 || strncmp(name, "Xapic_isr", 9) == 0) frame_type = INTERRUPT; else if (strcmp(name, "Xlcall_syscall") == 0 || strcmp(name, "Xint0x80_syscall") == 0) frame_type = SYSCALL; else if (strcmp(name, "dblfault_handler") == 0) frame_type = DOUBLE_FAULT; + /* XXX: These are interrupts with trap frames. */ + else if (strcmp(name, "Xtimerint") == 0 || + strcmp(name, "Xcpustop") == 0 || + strcmp(name, "Xrendezvous") == 0 || + strcmp(name, "Xipi_intr_bitmap_handler") == 0 || + strcmp(name, "Xlazypmap") == 0) + frame_type = TRAP_INTERRUPT; } /* * Normal frames need no special processing. */ if (frame_type == NORMAL) { *ip = (db_addr_t) eip; *fp = (struct i386_frame *) ebp; return; } db_print_stack_entry(name, 0, 0, 0, eip); /* * For a double fault, we have to snag the values from the * previous TSS since a double fault uses a task gate to * switch to a known good state. */ if (frame_type == DOUBLE_FAULT) { esp = PCPU_GET(common_tss.tss_esp); eip = PCPU_GET(common_tss.tss_eip); ebp = PCPU_GET(common_tss.tss_ebp); db_printf( "--- trap 0x17, eip = %#r, esp = %#r, ebp = %#r ---\n", eip, esp, ebp); *ip = (db_addr_t) eip; *fp = (struct i386_frame *) ebp; return; } /* * Point to base of trapframe which is just above the * current frame. */ if (frame_type == INTERRUPT) tf = (struct trapframe *)((int)*fp + 12); else tf = (struct trapframe *)((int)*fp + 8); if (INKERNEL((int) tf)) { esp = get_esp(tf); eip = tf->tf_eip; ebp = tf->tf_ebp; switch (frame_type) { case TRAP: db_printf("--- trap %#r", tf->tf_trapno); break; case SYSCALL: db_printf("--- syscall"); decode_syscall(tf->tf_eax, td); break; + case TRAP_INTERRUPT: case INTERRUPT: db_printf("--- interrupt"); break; default: panic("The moon has moved again."); } db_printf(", eip = %#r, esp = %#r, ebp = %#r ---\n", eip, esp, ebp); } *ip = (db_addr_t) eip; *fp = (struct i386_frame *) ebp; } static int db_backtrace(struct thread *td, struct trapframe *tf, struct i386_frame *frame, db_addr_t pc, int count) { struct i386_frame *actframe; #define MAXNARG 16 char *argnames[MAXNARG], **argnp = NULL; const char *name; int *argp; db_expr_t offset; c_db_sym_t sym; int narg, quit; boolean_t first; if (count == -1) count = 1024; first = TRUE; quit = 0; db_setup_paging(db_simple_pager, &quit, db_lines_per_page); while (count-- && !quit) { sym = db_search_symbol(pc, DB_STGY_ANY, &offset); db_symbol_values(sym, &name, NULL); /* * Attempt to determine a (possibly fake) frame that gives * the caller's pc. It may differ from `frame' if the * current function never sets up a standard frame or hasn't * set one up yet or has just discarded one. The last two * cases can be guessed fairly reliably for code generated * by gcc. The first case is too much trouble to handle in * general because the amount of junk on the stack depends * on the pc (the special handling of "calltrap", etc. in * db_nextframe() works because the `next' pc is special). */ actframe = frame; if (first) { if (tf != NULL) { int instr; instr = db_get_value(pc, 4, FALSE); if ((instr & 0xffffff) == 0x00e58955) { /* pushl %ebp; movl %esp, %ebp */ actframe = (void *)(get_esp(tf) - 4); } else if ((instr & 0xffff) == 0x0000e589) { /* movl %esp, %ebp */ actframe = (void *)get_esp(tf); if (tf->tf_ebp == 0) { /* Fake frame better. */ frame = actframe; } } else if ((instr & 0xff) == 0x000000c3) { /* ret */ actframe = (void *)(get_esp(tf) - 4); } else if (offset == 0) { /* Probably an assembler symbol. */ actframe = (void *)(get_esp(tf) - 4); } } else if (strcmp(name, "fork_trampoline") == 0) { /* * Don't try to walk back on a stack for a * process that hasn't actually been run yet. */ db_print_stack_entry(name, 0, 0, 0, pc); break; } first = FALSE; } argp = &actframe->f_arg0; narg = MAXNARG; if (sym != NULL && db_sym_numargs(sym, &narg, argnames)) { argnp = argnames; } else { narg = db_numargs(frame); } db_print_stack_entry(name, narg, argnp, argp, pc); if (actframe != frame) { /* `frame' belongs to caller. */ pc = (db_addr_t) db_get_value((int)&actframe->f_retaddr, 4, FALSE); continue; } db_nextframe(&frame, &pc, td); if (INKERNEL((int)pc) && !INKERNEL((int) frame)) { sym = db_search_symbol(pc, DB_STGY_ANY, &offset); db_symbol_values(sym, &name, NULL); db_print_stack_entry(name, 0, 0, 0, pc); break; } if (!INKERNEL((int) frame)) { break; } } return (0); } void db_trace_self(void) { struct i386_frame *frame; db_addr_t callpc; register_t ebp; __asm __volatile("movl %%ebp,%0" : "=r" (ebp)); frame = (struct i386_frame *)ebp; callpc = (db_addr_t)db_get_value((int)&frame->f_retaddr, 4, FALSE); frame = frame->f_frame; db_backtrace(curthread, NULL, frame, callpc, -1); } int db_trace_thread(struct thread *thr, int count) { struct pcb *ctx; ctx = kdb_thr_ctx(thr); return (db_backtrace(thr, NULL, (struct i386_frame *)ctx->pcb_ebp, ctx->pcb_eip, count)); } void stack_save(struct stack *st) { struct i386_frame *frame; vm_offset_t callpc; register_t ebp; stack_zero(st); __asm __volatile("movl %%ebp,%0" : "=r" (ebp)); frame = (struct i386_frame *)ebp; while (1) { if (!INKERNEL(frame)) break; callpc = frame->f_retaddr; if (!INKERNEL(callpc)) break; if (stack_put(st, callpc) == -1) break; frame = frame->f_frame; } } int i386_set_watch(watchnum, watchaddr, size, access, d) int watchnum; unsigned int watchaddr; int size; int access; struct dbreg * d; { int i; unsigned int mask; if (watchnum == -1) { for (i = 0, mask = 0x3; i < 4; i++, mask <<= 2) if ((d->dr[7] & mask) == 0) break; if (i < 4) watchnum = i; else return (-1); } switch (access) { case DBREG_DR7_EXEC: size = 1; /* size must be 1 for an execution breakpoint */ /* fall through */ case DBREG_DR7_WRONLY: case DBREG_DR7_RDWR: break; default : return (-1); } /* * we can watch a 1, 2, or 4 byte sized location */ switch (size) { case 1 : mask = 0x00; break; case 2 : mask = 0x01 << 2; break; case 4 : mask = 0x03 << 2; break; default : return (-1); } mask |= access; /* clear the bits we are about to affect */ d->dr[7] &= ~((0x3 << (watchnum*2)) | (0x0f << (watchnum*4+16))); /* set drN register to the address, N=watchnum */ DBREG_DRX(d,watchnum) = watchaddr; /* enable the watchpoint */ d->dr[7] |= (0x2 << (watchnum*2)) | (mask << (watchnum*4+16)); return (watchnum); } int i386_clr_watch(watchnum, d) int watchnum; struct dbreg * d; { if (watchnum < 0 || watchnum >= 4) return (-1); d->dr[7] = d->dr[7] & ~((0x3 << (watchnum*2)) | (0x0f << (watchnum*4+16))); DBREG_DRX(d,watchnum) = 0; return (0); } int db_md_set_watchpoint(addr, size) db_expr_t addr; db_expr_t size; { int avail, wsize; int i; struct dbreg d; fill_dbregs(NULL, &d); avail = 0; for(i=0; i<4; i++) { if ((d.dr[7] & (3 << (i*2))) == 0) avail++; } if (avail*4 < size) return (-1); for (i=0; i<4 && (size != 0); i++) { if ((d.dr[7] & (3<<(i*2))) == 0) { if (size > 4) wsize = 4; else wsize = size; if (wsize == 3) wsize++; i386_set_watch(i, addr, wsize, DBREG_DR7_WRONLY, &d); addr += wsize; size -= wsize; } } set_dbregs(NULL, &d); return(0); } int db_md_clr_watchpoint(addr, size) db_expr_t addr; db_expr_t size; { int i; struct dbreg d; fill_dbregs(NULL, &d); for(i=0; i<4; i++) { if (d.dr[7] & (3 << (i*2))) { if ((DBREG_DRX((&d), i) >= addr) && (DBREG_DRX((&d), i) < addr+size)) i386_clr_watch(i, &d); } } set_dbregs(NULL, &d); return(0); } static char * watchtype_str(type) int type; { switch (type) { case DBREG_DR7_EXEC : return "execute"; break; case DBREG_DR7_RDWR : return "read/write"; break; case DBREG_DR7_WRONLY : return "write"; break; default : return "invalid"; break; } } void db_md_list_watchpoints() { int i; struct dbreg d; fill_dbregs(NULL, &d); db_printf("\nhardware watchpoints:\n"); db_printf(" watch status type len address\n"); db_printf(" ----- -------- ---------- --- ----------\n"); for (i=0; i<4; i++) { if (d.dr[7] & (0x03 << (i*2))) { unsigned type, len; type = (d.dr[7] >> (16+(i*4))) & 3; len = (d.dr[7] >> (16+(i*4)+2)) & 3; db_printf(" %-5d %-8s %10s %3d 0x%08x\n", i, "enabled", watchtype_str(type), len+1, DBREG_DRX((&d),i)); } else { db_printf(" %-5d disabled\n", i); } } db_printf("\ndebug register values:\n"); for (i=0; i<8; i++) { db_printf(" dr%d 0x%08x\n", i, DBREG_DRX((&d),i)); } db_printf("\n"); } Index: head/sys/i386/i386/intr_machdep.c =================================================================== --- head/sys/i386/i386/intr_machdep.c (revision 153145) +++ head/sys/i386/i386/intr_machdep.c (revision 153146) @@ -1,337 +1,337 @@ /*- * Copyright (c) 2003 John Baldwin * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the author nor the names of any co-contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ /* * Machine dependent interrupt code for i386. For the i386, we have to * deal with different PICs. Thus, we use the passed in vector to lookup * an interrupt source associated with that vector. The interrupt source * describes which PIC the source belongs to and includes methods to handle * that source. */ #include "opt_ddb.h" #include #include #include #include #include #include #include #include #include #include #include #include #ifdef DDB #include #endif #define MAX_STRAY_LOG 5 typedef void (*mask_fn)(void *); static int intrcnt_index; static struct intsrc *interrupt_sources[NUM_IO_INTS]; static struct mtx intr_table_lock; static void intr_init(void *__dummy); static void intrcnt_setname(const char *name, int index); static void intrcnt_updatename(struct intsrc *is); static void intrcnt_register(struct intsrc *is); /* * Register a new interrupt source with the global interrupt system. * The global interrupts need to be disabled when this function is * called. */ int intr_register_source(struct intsrc *isrc) { int error, vector; vector = isrc->is_pic->pic_vector(isrc); if (interrupt_sources[vector] != NULL) return (EEXIST); error = intr_event_create(&isrc->is_event, isrc, 0, (mask_fn)isrc->is_pic->pic_enable_source, "irq%d:", vector); if (error) return (error); mtx_lock_spin(&intr_table_lock); if (interrupt_sources[vector] != NULL) { mtx_unlock_spin(&intr_table_lock); intr_event_destroy(isrc->is_event); return (EEXIST); } intrcnt_register(isrc); interrupt_sources[vector] = isrc; mtx_unlock_spin(&intr_table_lock); return (0); } struct intsrc * intr_lookup_source(int vector) { return (interrupt_sources[vector]); } int intr_add_handler(const char *name, int vector, driver_intr_t handler, void *arg, enum intr_type flags, void **cookiep) { struct intsrc *isrc; int error; isrc = intr_lookup_source(vector); if (isrc == NULL) return (EINVAL); error = intr_event_add_handler(isrc->is_event, name, handler, arg, intr_priority(flags), flags, cookiep); if (error == 0) { intrcnt_updatename(isrc); isrc->is_pic->pic_enable_intr(isrc); isrc->is_pic->pic_enable_source(isrc); } return (error); } int intr_remove_handler(void *cookie) { int error; error = intr_event_remove_handler(cookie); #ifdef XXX if (error == 0) intrcnt_updatename(/* XXX */); #endif return (error); } int intr_config_intr(int vector, enum intr_trigger trig, enum intr_polarity pol) { struct intsrc *isrc; isrc = intr_lookup_source(vector); if (isrc == NULL) return (EINVAL); return (isrc->is_pic->pic_config_intr(isrc, trig, pol)); } void -intr_execute_handlers(struct intsrc *isrc, struct intrframe *iframe) +intr_execute_handlers(struct intsrc *isrc, struct trapframe *frame) { struct thread *td; struct intr_event *ie; struct intr_handler *ih; int error, vector, thread; td = curthread; /* * We count software interrupts when we process them. The * code here follows previous practice, but there's an * argument for counting hardware interrupts when they're * processed too. */ (*isrc->is_count)++; PCPU_LAZY_INC(cnt.v_intr); ie = isrc->is_event; /* * XXX: We assume that IRQ 0 is only used for the ISA timer * device (clk). */ vector = isrc->is_pic->pic_vector(isrc); if (vector == 0) clkintr_pending = 1; /* * For stray interrupts, mask and EOI the source, bump the * stray count, and log the condition. */ if (ie == NULL || TAILQ_EMPTY(&ie->ie_handlers)) { isrc->is_pic->pic_disable_source(isrc, PIC_EOI); (*isrc->is_straycount)++; if (*isrc->is_straycount < MAX_STRAY_LOG) log(LOG_ERR, "stray irq%d\n", vector); else if (*isrc->is_straycount == MAX_STRAY_LOG) log(LOG_CRIT, "too many stray irq %d's: not logging anymore\n", vector); return; } /* * Execute fast interrupt handlers directly. * To support clock handlers, if a handler registers * with a NULL argument, then we pass it a pointer to - * an intrframe as its argument. + * a trapframe as its argument. */ td->td_intr_nesting_level++; thread = 0; critical_enter(); TAILQ_FOREACH(ih, &ie->ie_handlers, ih_next) { if (!(ih->ih_flags & IH_FAST)) { thread = 1; continue; } CTR4(KTR_INTR, "%s: exec %p(%p) for %s", __func__, - ih->ih_handler, ih->ih_argument == NULL ? iframe : + ih->ih_handler, ih->ih_argument == NULL ? frame : ih->ih_argument, ih->ih_name); if (ih->ih_argument == NULL) - ih->ih_handler(iframe); + ih->ih_handler(frame); else ih->ih_handler(ih->ih_argument); } /* * If there are any threaded handlers that need to run, * mask the source as well as sending it an EOI. Otherwise, * just send it an EOI but leave it unmasked. */ if (thread) isrc->is_pic->pic_disable_source(isrc, PIC_EOI); else isrc->is_pic->pic_eoi_source(isrc); critical_exit(); /* Schedule the ithread if needed. */ if (thread) { error = intr_event_schedule_thread(ie); KASSERT(error == 0, ("bad stray interrupt")); } td->td_intr_nesting_level--; } void intr_resume(void) { struct intsrc **isrc; int i; mtx_lock_spin(&intr_table_lock); for (i = 0, isrc = interrupt_sources; i < NUM_IO_INTS; i++, isrc++) if (*isrc != NULL && (*isrc)->is_pic->pic_resume != NULL) (*isrc)->is_pic->pic_resume(*isrc); mtx_unlock_spin(&intr_table_lock); } void intr_suspend(void) { struct intsrc **isrc; int i; mtx_lock_spin(&intr_table_lock); for (i = 0, isrc = interrupt_sources; i < NUM_IO_INTS; i++, isrc++) if (*isrc != NULL && (*isrc)->is_pic->pic_suspend != NULL) (*isrc)->is_pic->pic_suspend(*isrc); mtx_unlock_spin(&intr_table_lock); } static void intrcnt_setname(const char *name, int index) { snprintf(intrnames + (MAXCOMLEN + 1) * index, MAXCOMLEN + 1, "%-*s", MAXCOMLEN, name); } static void intrcnt_updatename(struct intsrc *is) { intrcnt_setname(is->is_event->ie_fullname, is->is_index); } static void intrcnt_register(struct intsrc *is) { char straystr[MAXCOMLEN + 1]; /* mtx_assert(&intr_table_lock, MA_OWNED); */ KASSERT(is->is_event != NULL, ("%s: isrc with no event", __func__)); is->is_index = intrcnt_index; intrcnt_index += 2; snprintf(straystr, MAXCOMLEN + 1, "stray irq%d", is->is_pic->pic_vector(is)); intrcnt_updatename(is); is->is_count = &intrcnt[is->is_index]; intrcnt_setname(straystr, is->is_index + 1); is->is_straycount = &intrcnt[is->is_index + 1]; } void intrcnt_add(const char *name, u_long **countp) { mtx_lock_spin(&intr_table_lock); *countp = &intrcnt[intrcnt_index]; intrcnt_setname(name, intrcnt_index); intrcnt_index++; mtx_unlock_spin(&intr_table_lock); } static void intr_init(void *dummy __unused) { intrcnt_setname("???", 0); intrcnt_index = 1; mtx_init(&intr_table_lock, "intr table", NULL, MTX_SPIN); } SYSINIT(intr_init, SI_SUB_INTR, SI_ORDER_FIRST, intr_init, NULL) #ifdef DDB /* * Dump data about interrupt handlers */ DB_SHOW_COMMAND(irqs, db_show_irqs) { struct intsrc **isrc; int i, quit, verbose; quit = 0; if (strcmp(modif, "v") == 0) verbose = 1; else verbose = 0; isrc = interrupt_sources; db_setup_paging(db_simple_pager, &quit, db_lines_per_page); for (i = 0; i < NUM_IO_INTS && !quit; i++, isrc++) if (*isrc != NULL) db_dump_intr_event((*isrc)->is_event, verbose); } #endif Index: head/sys/i386/i386/local_apic.c =================================================================== --- head/sys/i386/i386/local_apic.c (revision 153145) +++ head/sys/i386/i386/local_apic.c (revision 153146) @@ -1,1050 +1,1050 @@ /*- * Copyright (c) 2003 John Baldwin * Copyright (c) 1996, by Steve Passe * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. The name of the developer may NOT be used to endorse or promote products * derived from this software without specific prior written permission. * 3. Neither the name of the author nor the names of any co-contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * Local APIC support on Pentium and later processors. */ #include __FBSDID("$FreeBSD$"); #include "opt_hwpmc_hooks.h" #include "opt_ddb.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef DDB #include #include #endif /* * We can handle up to 60 APICs via our logical cluster IDs, but currently * the physical IDs on Intel processors up to the Pentium 4 are limited to * 16. */ #define MAX_APICID 16 /* Sanity checks on IDT vectors. */ CTASSERT(APIC_IO_INTS + APIC_NUM_IOINTS == APIC_TIMER_INT); CTASSERT(APIC_TIMER_INT < APIC_LOCAL_INTS); CTASSERT(APIC_LOCAL_INTS == 240); CTASSERT(IPI_STOP < APIC_SPURIOUS_INT); #define LAPIC_TIMER_HZ_DIVIDER 2 #define LAPIC_TIMER_STATHZ_DIVIDER 15 #define LAPIC_TIMER_PROFHZ_DIVIDER 3 /* Magic IRQ values for the timer and syscalls. */ #define IRQ_TIMER (NUM_IO_INTS + 1) #define IRQ_SYSCALL (NUM_IO_INTS + 2) /* * Support for local APICs. Local APICs manage interrupts on each * individual processor as opposed to I/O APICs which receive interrupts * from I/O devices and then forward them on to the local APICs. * * Local APICs can also send interrupts to each other thus providing the * mechanism for IPIs. */ struct lvt { u_int lvt_edgetrigger:1; u_int lvt_activehi:1; u_int lvt_masked:1; u_int lvt_active:1; u_int lvt_mode:16; u_int lvt_vector:8; }; struct lapic { struct lvt la_lvts[LVT_MAX + 1]; u_int la_id:8; u_int la_cluster:4; u_int la_cluster_id:2; u_int la_present:1; u_long *la_timer_count; u_long la_hard_ticks; u_long la_stat_ticks; u_long la_prof_ticks; } static lapics[MAX_APICID]; /* XXX: should thermal be an NMI? */ /* Global defaults for local APIC LVT entries. */ static struct lvt lvts[LVT_MAX + 1] = { { 1, 1, 1, 1, APIC_LVT_DM_EXTINT, 0 }, /* LINT0: masked ExtINT */ { 1, 1, 0, 1, APIC_LVT_DM_NMI, 0 }, /* LINT1: NMI */ { 1, 1, 1, 1, APIC_LVT_DM_FIXED, APIC_TIMER_INT }, /* Timer */ { 1, 1, 1, 1, APIC_LVT_DM_FIXED, APIC_ERROR_INT }, /* Error */ { 1, 1, 0, 1, APIC_LVT_DM_NMI, 0 }, /* PMC */ { 1, 1, 1, 1, APIC_LVT_DM_FIXED, APIC_THERMAL_INT }, /* Thermal */ }; static inthand_t *ioint_handlers[] = { NULL, /* 0 - 31 */ IDTVEC(apic_isr1), /* 32 - 63 */ IDTVEC(apic_isr2), /* 64 - 95 */ IDTVEC(apic_isr3), /* 96 - 127 */ IDTVEC(apic_isr4), /* 128 - 159 */ IDTVEC(apic_isr5), /* 160 - 191 */ IDTVEC(apic_isr6), /* 192 - 223 */ IDTVEC(apic_isr7), /* 224 - 255 */ }; /* Include IDT_SYSCALL to make indexing easier. */ static u_int ioint_irqs[APIC_NUM_IOINTS + 1]; static u_int32_t lapic_timer_divisors[] = { APIC_TDCR_1, APIC_TDCR_2, APIC_TDCR_4, APIC_TDCR_8, APIC_TDCR_16, APIC_TDCR_32, APIC_TDCR_64, APIC_TDCR_128 }; volatile lapic_t *lapic; static u_long lapic_timer_divisor, lapic_timer_period, lapic_timer_hz; static void lapic_enable(void); static void lapic_timer_enable_intr(void); static void lapic_timer_oneshot(u_int count); static void lapic_timer_periodic(u_int count); static void lapic_timer_set_divisor(u_int divisor); static uint32_t lvt_mode(struct lapic *la, u_int pin, uint32_t value); static uint32_t lvt_mode(struct lapic *la, u_int pin, uint32_t value) { struct lvt *lvt; KASSERT(pin <= LVT_MAX, ("%s: pin %u out of range", __func__, pin)); if (la->la_lvts[pin].lvt_active) lvt = &la->la_lvts[pin]; else lvt = &lvts[pin]; value &= ~(APIC_LVT_M | APIC_LVT_TM | APIC_LVT_IIPP | APIC_LVT_DM | APIC_LVT_VECTOR); if (lvt->lvt_edgetrigger == 0) value |= APIC_LVT_TM; if (lvt->lvt_activehi == 0) value |= APIC_LVT_IIPP_INTALO; if (lvt->lvt_masked) value |= APIC_LVT_M; value |= lvt->lvt_mode; switch (lvt->lvt_mode) { case APIC_LVT_DM_NMI: case APIC_LVT_DM_SMI: case APIC_LVT_DM_INIT: case APIC_LVT_DM_EXTINT: if (!lvt->lvt_edgetrigger) { printf("lapic%u: Forcing LINT%u to edge trigger\n", la->la_id, pin); value |= APIC_LVT_TM; } /* Use a vector of 0. */ break; case APIC_LVT_DM_FIXED: value |= lvt->lvt_vector; break; default: panic("bad APIC LVT delivery mode: %#x\n", value); } return (value); } /* * Map the local APIC and setup necessary interrupt vectors. */ void lapic_init(uintptr_t addr) { /* Map the local APIC and setup the spurious interrupt handler. */ KASSERT(trunc_page(addr) == addr, ("local APIC not aligned on a page boundary")); lapic = (lapic_t *)pmap_mapdev(addr, sizeof(lapic_t)); setidt(APIC_SPURIOUS_INT, IDTVEC(spuriousint), SDT_SYS386IGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); /* Perform basic initialization of the BSP's local APIC. */ lapic_enable(); ioint_irqs[IDT_SYSCALL - APIC_IO_INTS] = IRQ_SYSCALL; /* Set BSP's per-CPU local APIC ID. */ PCPU_SET(apic_id, lapic_id()); /* Local APIC timer interrupt. */ setidt(APIC_TIMER_INT, IDTVEC(timerint), SDT_SYS386IGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); ioint_irqs[APIC_TIMER_INT - APIC_IO_INTS] = IRQ_TIMER; /* XXX: error/thermal interrupts */ } /* * Create a local APIC instance. */ void lapic_create(u_int apic_id, int boot_cpu) { int i; if (apic_id >= MAX_APICID) { printf("APIC: Ignoring local APIC with ID %d\n", apic_id); if (boot_cpu) panic("Can't ignore BSP"); return; } KASSERT(!lapics[apic_id].la_present, ("duplicate local APIC %u", apic_id)); /* * Assume no local LVT overrides and a cluster of 0 and * intra-cluster ID of 0. */ lapics[apic_id].la_present = 1; lapics[apic_id].la_id = apic_id; for (i = 0; i < LVT_MAX; i++) { lapics[apic_id].la_lvts[i] = lvts[i]; lapics[apic_id].la_lvts[i].lvt_active = 0; } #ifdef SMP cpu_add(apic_id, boot_cpu); #endif } /* * Dump contents of local APIC registers */ void lapic_dump(const char* str) { printf("cpu%d %s:\n", PCPU_GET(cpuid), str); printf(" ID: 0x%08x VER: 0x%08x LDR: 0x%08x DFR: 0x%08x\n", lapic->id, lapic->version, lapic->ldr, lapic->dfr); printf(" lint0: 0x%08x lint1: 0x%08x TPR: 0x%08x SVR: 0x%08x\n", lapic->lvt_lint0, lapic->lvt_lint1, lapic->tpr, lapic->svr); printf(" timer: 0x%08x therm: 0x%08x err: 0x%08x pcm: 0x%08x\n", lapic->lvt_timer, lapic->lvt_thermal, lapic->lvt_error, lapic->lvt_pcint); } void lapic_setup(void) { struct lapic *la; u_int32_t value, maxlvt; register_t eflags; char buf[MAXCOMLEN + 1]; la = &lapics[lapic_id()]; KASSERT(la->la_present, ("missing APIC structure")); eflags = intr_disable(); maxlvt = (lapic->version & APIC_VER_MAXLVT) >> MAXLVTSHIFT; /* Initialize the TPR to allow all interrupts. */ lapic_set_tpr(0); /* Use the cluster model for logical IDs. */ value = lapic->dfr; value &= ~APIC_DFR_MODEL_MASK; value |= APIC_DFR_MODEL_CLUSTER; lapic->dfr = value; /* Set this APIC's logical ID. */ value = lapic->ldr; value &= ~APIC_ID_MASK; value |= (la->la_cluster << APIC_ID_CLUSTER_SHIFT | 1 << la->la_cluster_id) << APIC_ID_SHIFT; lapic->ldr = value; /* Setup spurious vector and enable the local APIC. */ lapic_enable(); /* Program LINT[01] LVT entries. */ lapic->lvt_lint0 = lvt_mode(la, LVT_LINT0, lapic->lvt_lint0); lapic->lvt_lint1 = lvt_mode(la, LVT_LINT1, lapic->lvt_lint1); #ifdef HWPMC_HOOKS /* Program the PMC LVT entry if present. */ if (maxlvt >= LVT_PMC) lapic->lvt_pcint = lvt_mode(la, LVT_PMC, lapic->lvt_pcint); #endif /* Program timer LVT and setup handler. */ lapic->lvt_timer = lvt_mode(la, LVT_TIMER, lapic->lvt_timer); snprintf(buf, sizeof(buf), "cpu%d: timer", PCPU_GET(cpuid)); intrcnt_add(buf, &la->la_timer_count); if (PCPU_GET(cpuid) != 0) { KASSERT(lapic_timer_period != 0, ("lapic%u: zero divisor", lapic_id())); lapic_timer_set_divisor(lapic_timer_divisor); lapic_timer_periodic(lapic_timer_period); lapic_timer_enable_intr(); } /* XXX: Error and thermal LVTs */ intr_restore(eflags); } /* * Called by cpu_initclocks() on the BSP to setup the local APIC timer so * that it can drive hardclock, statclock, and profclock. This function * returns true if it is able to use the local APIC timer to drive the * clocks and false if it is not able. */ int lapic_setup_clock(void) { u_long value; /* Can't drive the timer without a local APIC. */ if (lapic == NULL) return (0); /* Start off with a divisor of 2 (power on reset default). */ lapic_timer_divisor = 2; /* Try to calibrate the local APIC timer. */ do { lapic_timer_set_divisor(lapic_timer_divisor); lapic_timer_oneshot(APIC_TIMER_MAX_COUNT); DELAY(2000000); value = APIC_TIMER_MAX_COUNT - lapic->ccr_timer; if (value != APIC_TIMER_MAX_COUNT) break; lapic_timer_divisor <<= 1; } while (lapic_timer_divisor <= 128); if (lapic_timer_divisor > 128) panic("lapic: Divisor too big"); value /= 2; if (bootverbose) printf("lapic: Divisor %lu, Frequency %lu hz\n", lapic_timer_divisor, value); /* * We will drive the timer at a small multiple of hz and drive * both of the other timers with similarly small but relatively * prime divisors. */ lapic_timer_hz = hz * LAPIC_TIMER_HZ_DIVIDER; stathz = lapic_timer_hz / LAPIC_TIMER_STATHZ_DIVIDER; profhz = lapic_timer_hz / LAPIC_TIMER_PROFHZ_DIVIDER; lapic_timer_period = value / lapic_timer_hz; /* * Start up the timer on the BSP. The APs will kick off their * timer during lapic_setup(). */ lapic_timer_periodic(lapic_timer_period); lapic_timer_enable_intr(); return (1); } void lapic_disable(void) { uint32_t value; /* Software disable the local APIC. */ value = lapic->svr; value &= ~APIC_SVR_SWEN; lapic->svr = value; } static void lapic_enable(void) { u_int32_t value; /* Program the spurious vector to enable the local APIC. */ value = lapic->svr; value &= ~(APIC_SVR_VECTOR | APIC_SVR_FOCUS); value |= (APIC_SVR_FEN | APIC_SVR_SWEN | APIC_SPURIOUS_INT); lapic->svr = value; } int lapic_id(void) { KASSERT(lapic != NULL, ("local APIC is not mapped")); return (lapic->id >> APIC_ID_SHIFT); } int lapic_intr_pending(u_int vector) { volatile u_int32_t *irr; /* * The IRR registers are an array of 128-bit registers each of * which only describes 32 interrupts in the low 32 bits.. Thus, * we divide the vector by 32 to get the 128-bit index. We then * multiply that index by 4 to get the equivalent index from * treating the IRR as an array of 32-bit registers. Finally, we * modulus the vector by 32 to determine the individual bit to * test. */ irr = &lapic->irr0; return (irr[(vector / 32) * 4] & 1 << (vector % 32)); } void lapic_set_logical_id(u_int apic_id, u_int cluster, u_int cluster_id) { struct lapic *la; KASSERT(lapics[apic_id].la_present, ("%s: APIC %u doesn't exist", __func__, apic_id)); KASSERT(cluster <= APIC_MAX_CLUSTER, ("%s: cluster %u too big", __func__, cluster)); KASSERT(cluster_id <= APIC_MAX_INTRACLUSTER_ID, ("%s: intra cluster id %u too big", __func__, cluster_id)); la = &lapics[apic_id]; la->la_cluster = cluster; la->la_cluster_id = cluster_id; } int lapic_set_lvt_mask(u_int apic_id, u_int pin, u_char masked) { if (pin > LVT_MAX) return (EINVAL); if (apic_id == APIC_ID_ALL) { lvts[pin].lvt_masked = masked; if (bootverbose) printf("lapic:"); } else { KASSERT(lapics[apic_id].la_present, ("%s: missing APIC %u", __func__, apic_id)); lapics[apic_id].la_lvts[pin].lvt_masked = masked; lapics[apic_id].la_lvts[pin].lvt_active = 1; if (bootverbose) printf("lapic%u:", apic_id); } if (bootverbose) printf(" LINT%u %s\n", pin, masked ? "masked" : "unmasked"); return (0); } int lapic_set_lvt_mode(u_int apic_id, u_int pin, u_int32_t mode) { struct lvt *lvt; if (pin > LVT_MAX) return (EINVAL); if (apic_id == APIC_ID_ALL) { lvt = &lvts[pin]; if (bootverbose) printf("lapic:"); } else { KASSERT(lapics[apic_id].la_present, ("%s: missing APIC %u", __func__, apic_id)); lvt = &lapics[apic_id].la_lvts[pin]; lvt->lvt_active = 1; if (bootverbose) printf("lapic%u:", apic_id); } lvt->lvt_mode = mode; switch (mode) { case APIC_LVT_DM_NMI: case APIC_LVT_DM_SMI: case APIC_LVT_DM_INIT: case APIC_LVT_DM_EXTINT: lvt->lvt_edgetrigger = 1; lvt->lvt_activehi = 1; if (mode == APIC_LVT_DM_EXTINT) lvt->lvt_masked = 1; else lvt->lvt_masked = 0; break; default: panic("Unsupported delivery mode: 0x%x\n", mode); } if (bootverbose) { printf(" Routing "); switch (mode) { case APIC_LVT_DM_NMI: printf("NMI"); break; case APIC_LVT_DM_SMI: printf("SMI"); break; case APIC_LVT_DM_INIT: printf("INIT"); break; case APIC_LVT_DM_EXTINT: printf("ExtINT"); break; } printf(" -> LINT%u\n", pin); } return (0); } int lapic_set_lvt_polarity(u_int apic_id, u_int pin, enum intr_polarity pol) { if (pin > LVT_MAX || pol == INTR_POLARITY_CONFORM) return (EINVAL); if (apic_id == APIC_ID_ALL) { lvts[pin].lvt_activehi = (pol == INTR_POLARITY_HIGH); if (bootverbose) printf("lapic:"); } else { KASSERT(lapics[apic_id].la_present, ("%s: missing APIC %u", __func__, apic_id)); lapics[apic_id].la_lvts[pin].lvt_active = 1; lapics[apic_id].la_lvts[pin].lvt_activehi = (pol == INTR_POLARITY_HIGH); if (bootverbose) printf("lapic%u:", apic_id); } if (bootverbose) printf(" LINT%u polarity: %s\n", pin, pol == INTR_POLARITY_HIGH ? "high" : "low"); return (0); } int lapic_set_lvt_triggermode(u_int apic_id, u_int pin, enum intr_trigger trigger) { if (pin > LVT_MAX || trigger == INTR_TRIGGER_CONFORM) return (EINVAL); if (apic_id == APIC_ID_ALL) { lvts[pin].lvt_edgetrigger = (trigger == INTR_TRIGGER_EDGE); if (bootverbose) printf("lapic:"); } else { KASSERT(lapics[apic_id].la_present, ("%s: missing APIC %u", __func__, apic_id)); lapics[apic_id].la_lvts[pin].lvt_edgetrigger = (trigger == INTR_TRIGGER_EDGE); lapics[apic_id].la_lvts[pin].lvt_active = 1; if (bootverbose) printf("lapic%u:", apic_id); } if (bootverbose) printf(" LINT%u trigger: %s\n", pin, trigger == INTR_TRIGGER_EDGE ? "edge" : "level"); return (0); } /* * Adjust the TPR of the current CPU so that it blocks all interrupts below * the passed in vector. */ void lapic_set_tpr(u_int vector) { #ifdef CHEAP_TPR lapic->tpr = vector; #else u_int32_t tpr; tpr = lapic->tpr & ~APIC_TPR_PRIO; tpr |= vector; lapic->tpr = tpr; #endif } void lapic_eoi(void) { lapic->eoi = 0; } void -lapic_handle_intr(struct intrframe frame) +lapic_handle_intr(int vector, struct trapframe frame) { struct intsrc *isrc; - if (frame.if_vec == -1) + if (vector == -1) panic("Couldn't get vector from ISR!"); - isrc = intr_lookup_source(apic_idt_to_irq(frame.if_vec)); + isrc = intr_lookup_source(apic_idt_to_irq(vector)); intr_execute_handlers(isrc, &frame); } void lapic_handle_timer(struct clockframe frame) { struct lapic *la; /* Send EOI first thing. */ lapic_eoi(); /* Look up our local APIC structure for the tick counters. */ la = &lapics[PCPU_GET(apic_id)]; (*la->la_timer_count)++; critical_enter(); /* Fire hardclock at hz. */ la->la_hard_ticks += hz; if (la->la_hard_ticks >= lapic_timer_hz) { la->la_hard_ticks -= lapic_timer_hz; if (PCPU_GET(cpuid) == 0) hardclock(&frame); else hardclock_process(&frame); } /* Fire statclock at stathz. */ la->la_stat_ticks += stathz; if (la->la_stat_ticks >= lapic_timer_hz) { la->la_stat_ticks -= lapic_timer_hz; statclock(&frame); } /* Fire profclock at profhz, but only when needed. */ la->la_prof_ticks += profhz; if (la->la_prof_ticks >= lapic_timer_hz) { la->la_prof_ticks -= lapic_timer_hz; if (profprocs != 0) profclock(&frame); } critical_exit(); } static void lapic_timer_set_divisor(u_int divisor) { KASSERT(powerof2(divisor), ("lapic: invalid divisor %u", divisor)); KASSERT(ffs(divisor) <= sizeof(lapic_timer_divisors) / sizeof(u_int32_t), ("lapic: invalid divisor %u", divisor)); lapic->dcr_timer = lapic_timer_divisors[ffs(divisor) - 1]; } static void lapic_timer_oneshot(u_int count) { u_int32_t value; value = lapic->lvt_timer; value &= ~APIC_LVTT_TM; value |= APIC_LVTT_TM_ONE_SHOT; lapic->lvt_timer = value; lapic->icr_timer = count; } static void lapic_timer_periodic(u_int count) { u_int32_t value; value = lapic->lvt_timer; value &= ~APIC_LVTT_TM; value |= APIC_LVTT_TM_PERIODIC; lapic->lvt_timer = value; lapic->icr_timer = count; } static void lapic_timer_enable_intr(void) { u_int32_t value; value = lapic->lvt_timer; value &= ~APIC_LVT_M; lapic->lvt_timer = value; } /* Request a free IDT vector to be used by the specified IRQ. */ u_int apic_alloc_vector(u_int irq) { u_int vector; KASSERT(irq < NUM_IO_INTS, ("Invalid IRQ %u", irq)); /* * Search for a free vector. Currently we just use a very simple * algorithm to find the first free vector. */ mtx_lock_spin(&icu_lock); for (vector = 0; vector < APIC_NUM_IOINTS; vector++) { if (ioint_irqs[vector] != 0) continue; ioint_irqs[vector] = irq; mtx_unlock_spin(&icu_lock); return (vector + APIC_IO_INTS); } mtx_unlock_spin(&icu_lock); panic("Couldn't find an APIC vector for IRQ %u", irq); } void apic_enable_vector(u_int vector) { KASSERT(vector != IDT_SYSCALL, ("Attempt to overwrite syscall entry")); KASSERT(ioint_handlers[vector / 32] != NULL, ("No ISR handler for vector %u", vector)); setidt(vector, ioint_handlers[vector / 32], SDT_SYS386IGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); } /* Release an APIC vector when it's no longer in use. */ void apic_free_vector(u_int vector, u_int irq) { KASSERT(vector >= APIC_IO_INTS && vector != IDT_SYSCALL && vector <= APIC_IO_INTS + APIC_NUM_IOINTS, ("Vector %u does not map to an IRQ line", vector)); KASSERT(irq < NUM_IO_INTS, ("Invalid IRQ %u", irq)); KASSERT(ioint_irqs[vector - APIC_IO_INTS] == irq, ("IRQ mismatch")); mtx_lock_spin(&icu_lock); ioint_irqs[vector - APIC_IO_INTS] = 0; mtx_unlock_spin(&icu_lock); } /* Map an IDT vector (APIC) to an IRQ (interrupt source). */ u_int apic_idt_to_irq(u_int vector) { KASSERT(vector >= APIC_IO_INTS && vector != IDT_SYSCALL && vector <= APIC_IO_INTS + APIC_NUM_IOINTS, ("Vector %u does not map to an IRQ line", vector)); return (ioint_irqs[vector - APIC_IO_INTS]); } #ifdef DDB /* * Dump data about APIC IDT vector mappings. */ DB_SHOW_COMMAND(apic, db_show_apic) { struct intsrc *isrc; int quit, i, verbose; u_int irq; quit = 0; if (strcmp(modif, "vv") == 0) verbose = 2; else if (strcmp(modif, "v") == 0) verbose = 1; else verbose = 0; db_setup_paging(db_simple_pager, &quit, db_lines_per_page); for (i = 0; i < APIC_NUM_IOINTS + 1 && !quit; i++) { irq = ioint_irqs[i]; if (irq != 0 && irq != IRQ_SYSCALL) { db_printf("vec 0x%2x -> ", i + APIC_IO_INTS); if (irq == IRQ_TIMER) db_printf("lapic timer\n"); else if (irq < NUM_IO_INTS) { isrc = intr_lookup_source(irq); if (isrc == NULL || verbose == 0) db_printf("IRQ %u\n", irq); else db_dump_intr_event(isrc->is_event, verbose == 2); } else db_printf("IRQ %u ???\n", irq); } } } #endif /* * APIC probing support code. This includes code to manage enumerators. */ static SLIST_HEAD(, apic_enumerator) enumerators = SLIST_HEAD_INITIALIZER(enumerators); static struct apic_enumerator *best_enum; void apic_register_enumerator(struct apic_enumerator *enumerator) { #ifdef INVARIANTS struct apic_enumerator *apic_enum; SLIST_FOREACH(apic_enum, &enumerators, apic_next) { if (apic_enum == enumerator) panic("%s: Duplicate register of %s", __func__, enumerator->apic_name); } #endif SLIST_INSERT_HEAD(&enumerators, enumerator, apic_next); } /* * Probe the APIC enumerators, enumerate CPUs, and initialize the * local APIC. */ static void apic_init(void *dummy __unused) { struct apic_enumerator *enumerator; uint64_t apic_base; int retval, best; /* We only support built in local APICs. */ if (!(cpu_feature & CPUID_APIC)) return; /* Don't probe if APIC mode is disabled. */ if (resource_disabled("apic", 0)) return; /* First, probe all the enumerators to find the best match. */ best_enum = NULL; best = 0; SLIST_FOREACH(enumerator, &enumerators, apic_next) { retval = enumerator->apic_probe(); if (retval > 0) continue; if (best_enum == NULL || best < retval) { best_enum = enumerator; best = retval; } } if (best_enum == NULL) { if (bootverbose) printf("APIC: Could not find any APICs.\n"); return; } if (bootverbose) printf("APIC: Using the %s enumerator.\n", best_enum->apic_name); /* * To work around an errata, we disable the local APIC on some * CPUs during early startup. We need to turn the local APIC back * on on such CPUs now. */ if (cpu == CPU_686 && strcmp(cpu_vendor, "GenuineIntel") == 0 && (cpu_id & 0xff0) == 0x610) { apic_base = rdmsr(MSR_APICBASE); apic_base |= APICBASE_ENABLED; wrmsr(MSR_APICBASE, apic_base); } /* Second, probe the CPU's in the system. */ retval = best_enum->apic_probe_cpus(); if (retval != 0) printf("%s: Failed to probe CPUs: returned %d\n", best_enum->apic_name, retval); /* Third, initialize the local APIC. */ retval = best_enum->apic_setup_local(); if (retval != 0) printf("%s: Failed to setup the local APIC: returned %d\n", best_enum->apic_name, retval); #ifdef SMP /* Last, setup the cpu topology now that we have probed CPUs */ mp_topology(); #endif } SYSINIT(apic_init, SI_SUB_CPU, SI_ORDER_FIRST, apic_init, NULL) /* * Setup the I/O APICs. */ static void apic_setup_io(void *dummy __unused) { int retval; if (best_enum == NULL) return; retval = best_enum->apic_setup_io(); if (retval != 0) printf("%s: Failed to setup I/O APICs: returned %d\n", best_enum->apic_name, retval); /* * Finish setting up the local APIC on the BSP once we know how to * properly program the LINT pins. */ lapic_setup(); if (bootverbose) lapic_dump("BSP"); } SYSINIT(apic_setup_io, SI_SUB_INTR, SI_ORDER_SECOND, apic_setup_io, NULL) #ifdef SMP /* * Inter Processor Interrupt functions. The lapic_ipi_*() functions are * private to the sys/i386 code. The public interface for the rest of the * kernel is defined in mp_machdep.c. */ int lapic_ipi_wait(int delay) { int x, incr; /* * Wait delay loops for IPI to be sent. This is highly bogus * since this is sensitive to CPU clock speed. If delay is * -1, we wait forever. */ if (delay == -1) { incr = 0; delay = 1; } else incr = 1; for (x = 0; x < delay; x += incr) { if ((lapic->icr_lo & APIC_DELSTAT_MASK) == APIC_DELSTAT_IDLE) return (1); ia32_pause(); } return (0); } void lapic_ipi_raw(register_t icrlo, u_int dest) { register_t value, eflags; /* XXX: Need more sanity checking of icrlo? */ KASSERT(lapic != NULL, ("%s called too early", __func__)); KASSERT((dest & ~(APIC_ID_MASK >> APIC_ID_SHIFT)) == 0, ("%s: invalid dest field", __func__)); KASSERT((icrlo & APIC_ICRLO_RESV_MASK) == 0, ("%s: reserved bits set in ICR LO register", __func__)); /* Set destination in ICR HI register if it is being used. */ eflags = intr_disable(); if ((icrlo & APIC_DEST_MASK) == APIC_DEST_DESTFLD) { value = lapic->icr_hi; value &= ~APIC_ID_MASK; value |= dest << APIC_ID_SHIFT; lapic->icr_hi = value; } /* Program the contents of the IPI and dispatch it. */ value = lapic->icr_lo; value &= APIC_ICRLO_RESV_MASK; value |= icrlo; lapic->icr_lo = value; intr_restore(eflags); } #define BEFORE_SPIN 1000000 #ifdef DETECT_DEADLOCK #define AFTER_SPIN 1000 #endif void lapic_ipi_vectored(u_int vector, int dest) { register_t icrlo, destfield; KASSERT((vector & ~APIC_VECTOR_MASK) == 0, ("%s: invalid vector %d", __func__, vector)); icrlo = vector | APIC_DELMODE_FIXED | APIC_DESTMODE_PHY | APIC_LEVEL_DEASSERT | APIC_TRIGMOD_EDGE; destfield = 0; switch (dest) { case APIC_IPI_DEST_SELF: icrlo |= APIC_DEST_SELF; break; case APIC_IPI_DEST_ALL: icrlo |= APIC_DEST_ALLISELF; break; case APIC_IPI_DEST_OTHERS: icrlo |= APIC_DEST_ALLESELF; break; default: KASSERT((dest & ~(APIC_ID_MASK >> APIC_ID_SHIFT)) == 0, ("%s: invalid destination 0x%x", __func__, dest)); destfield = dest; } /* Wait for an earlier IPI to finish. */ if (!lapic_ipi_wait(BEFORE_SPIN)) { if (panicstr != NULL) return; else panic("APIC: Previous IPI is stuck"); } lapic_ipi_raw(icrlo, destfield); #ifdef DETECT_DEADLOCK /* Wait for IPI to be delivered. */ if (!lapic_ipi_wait(AFTER_SPIN)) { #ifdef needsattention /* * XXX FIXME: * * The above function waits for the message to actually be * delivered. It breaks out after an arbitrary timeout * since the message should eventually be delivered (at * least in theory) and that if it wasn't we would catch * the failure with the check above when the next IPI is * sent. * * We could skip this wait entirely, EXCEPT it probably * protects us from other routines that assume that the * message was delivered and acted upon when this function * returns. */ printf("APIC: IPI might be stuck\n"); #else /* !needsattention */ /* Wait until mesage is sent without a timeout. */ while (lapic->icr_lo & APIC_DELSTAT_PEND) ia32_pause(); #endif /* needsattention */ } #endif /* DETECT_DEADLOCK */ } #endif /* SMP */ Index: head/sys/i386/include/apicvar.h =================================================================== --- head/sys/i386/include/apicvar.h (revision 153145) +++ head/sys/i386/include/apicvar.h (revision 153146) @@ -1,217 +1,217 @@ /*- * Copyright (c) 2003 John Baldwin * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the author nor the names of any co-contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #ifndef _MACHINE_APICVAR_H_ #define _MACHINE_APICVAR_H_ /* * Local && I/O APIC variable definitions. */ /* * Layout of local APIC interrupt vectors: * * 0xff (255) +-------------+ * | | 15 (Spurious / IPIs / Local Interrupts) * 0xf0 (240) +-------------+ * | | 14 (I/O Interrupts / Timer) * 0xe0 (224) +-------------+ * | | 13 (I/O Interrupts) * 0xd0 (208) +-------------+ * | | 12 (I/O Interrupts) * 0xc0 (192) +-------------+ * | | 11 (I/O Interrupts) * 0xb0 (176) +-------------+ * | | 10 (I/O Interrupts) * 0xa0 (160) +-------------+ * | | 9 (I/O Interrupts) * 0x90 (144) +-------------+ * | | 8 (I/O Interrupts / System Calls) * 0x80 (128) +-------------+ * | | 7 (I/O Interrupts) * 0x70 (112) +-------------+ * | | 6 (I/O Interrupts) * 0x60 (96) +-------------+ * | | 5 (I/O Interrupts) * 0x50 (80) +-------------+ * | | 4 (I/O Interrupts) * 0x40 (64) +-------------+ * | | 3 (I/O Interrupts) * 0x30 (48) +-------------+ * | | 2 (ATPIC Interrupts) * 0x20 (32) +-------------+ * | | 1 (Exceptions, traps, faults, etc.) * 0x10 (16) +-------------+ * | | 0 (Exceptions, traps, faults, etc.) * 0x00 (0) +-------------+ * * Note: 0x80 needs to be handled specially and not allocated to an * I/O device! */ #define APIC_ID_ALL 0xff /* I/O Interrupts are used for external devices such as ISA, PCI, etc. */ #define APIC_IO_INTS (IDT_IO_INTS + 16) #define APIC_NUM_IOINTS 191 /* The timer interrupt is used for clock handling and drives hardclock, etc. */ #define APIC_TIMER_INT (APIC_IO_INTS + APIC_NUM_IOINTS) /* ********************* !!! WARNING !!! ****************************** * Each local apic has an interrupt receive fifo that is two entries deep * for each interrupt priority class (higher 4 bits of interrupt vector). * Once the fifo is full the APIC can no longer receive interrupts for this * class and sending IPIs from other CPUs will be blocked. * To avoid deadlocks there should be no more than two IPI interrupts * pending at the same time. * Currently this is guaranteed by dividing the IPIs in two groups that have * each at most one IPI interrupt pending. The first group is protected by the * smp_ipi_mtx and waits for the completion of the IPI (Only one IPI user * at a time) The second group uses a single interrupt and a bitmap to avoid * redundant IPI interrupts. * * Right now IPI_STOP used by kdb shares the interrupt priority class with * the two IPI groups mentioned above. As such IPI_STOP may cause a deadlock. * Eventually IPI_STOP should use NMI IPIs - this would eliminate this and * other deadlocks caused by IPI_STOP. */ /* Interrupts for local APIC LVT entries other than the timer. */ #define APIC_LOCAL_INTS 240 #define APIC_ERROR_INT APIC_LOCAL_INTS #define APIC_THERMAL_INT (APIC_LOCAL_INTS + 1) #define APIC_IPI_INTS (APIC_LOCAL_INTS + 2) #define IPI_RENDEZVOUS (APIC_IPI_INTS) /* Inter-CPU rendezvous. */ #define IPI_INVLTLB (APIC_IPI_INTS + 1) /* TLB Shootdown IPIs */ #define IPI_INVLPG (APIC_IPI_INTS + 2) #define IPI_INVLRNG (APIC_IPI_INTS + 3) #define IPI_LAZYPMAP (APIC_IPI_INTS + 4) /* Lazy pmap release. */ /* Vector to handle bitmap based IPIs */ #define IPI_BITMAP_VECTOR (APIC_IPI_INTS + 5) /* IPIs handled by IPI_BITMAPED_VECTOR (XXX ups is there a better place?) */ #define IPI_AST 0 /* Generate software trap. */ #define IPI_PREEMPT 1 #define IPI_BITMAP_LAST IPI_PREEMPT #define IPI_IS_BITMAPED(x) ((x) <= IPI_BITMAP_LAST) #define IPI_STOP (APIC_IPI_INTS + 6) /* Stop CPU until restarted. */ /* * The spurious interrupt can share the priority class with the IPIs since * it is not a normal interrupt. (Does not use the APIC's interrupt fifo) */ #define APIC_SPURIOUS_INT 255 #define LVT_LINT0 0 #define LVT_LINT1 1 #define LVT_TIMER 2 #define LVT_ERROR 3 #define LVT_PMC 4 #define LVT_THERMAL 5 #define LVT_MAX LVT_THERMAL #ifndef LOCORE #define APIC_IPI_DEST_SELF -1 #define APIC_IPI_DEST_ALL -2 #define APIC_IPI_DEST_OTHERS -3 #define APIC_BUS_UNKNOWN -1 #define APIC_BUS_ISA 0 #define APIC_BUS_EISA 1 #define APIC_BUS_PCI 2 #define APIC_BUS_MAX APIC_BUS_PCI /* * An APIC enumerator is a psuedo bus driver that enumerates APIC's including * CPU's and I/O APIC's. */ struct apic_enumerator { const char *apic_name; int (*apic_probe)(void); int (*apic_probe_cpus)(void); int (*apic_setup_local)(void); int (*apic_setup_io)(void); SLIST_ENTRY(apic_enumerator) apic_next; }; inthand_t IDTVEC(apic_isr1), IDTVEC(apic_isr2), IDTVEC(apic_isr3), IDTVEC(apic_isr4), IDTVEC(apic_isr5), IDTVEC(apic_isr6), IDTVEC(apic_isr7), IDTVEC(spuriousint), IDTVEC(timerint); u_int apic_alloc_vector(u_int irq); void apic_enable_vector(u_int vector); void apic_free_vector(u_int vector, u_int irq); u_int apic_idt_to_irq(u_int vector); void apic_register_enumerator(struct apic_enumerator *enumerator); void *ioapic_create(uintptr_t addr, int32_t id, int intbase); int ioapic_disable_pin(void *cookie, u_int pin); int ioapic_get_vector(void *cookie, u_int pin); int ioapic_next_logical_cluster(void); void ioapic_register(void *cookie); int ioapic_remap_vector(void *cookie, u_int pin, int vector); int ioapic_set_bus(void *cookie, u_int pin, int bus_type); int ioapic_set_extint(void *cookie, u_int pin); int ioapic_set_nmi(void *cookie, u_int pin); int ioapic_set_polarity(void *cookie, u_int pin, enum intr_polarity pol); int ioapic_set_triggermode(void *cookie, u_int pin, enum intr_trigger trigger); int ioapic_set_smi(void *cookie, u_int pin); void lapic_create(u_int apic_id, int boot_cpu); void lapic_disable(void); void lapic_dump(const char *str); void lapic_eoi(void); int lapic_id(void); void lapic_init(uintptr_t addr); int lapic_intr_pending(u_int vector); void lapic_ipi_raw(register_t icrlo, u_int dest); void lapic_ipi_vectored(u_int vector, int dest); int lapic_ipi_wait(int delay); -void lapic_handle_intr(struct intrframe frame); +void lapic_handle_intr(int vector, struct trapframe frame); void lapic_handle_timer(struct clockframe frame); void lapic_set_logical_id(u_int apic_id, u_int cluster, u_int cluster_id); int lapic_set_lvt_mask(u_int apic_id, u_int lvt, u_char masked); int lapic_set_lvt_mode(u_int apic_id, u_int lvt, u_int32_t mode); int lapic_set_lvt_polarity(u_int apic_id, u_int lvt, enum intr_polarity pol); int lapic_set_lvt_triggermode(u_int apic_id, u_int lvt, enum intr_trigger trigger); void lapic_set_tpr(u_int vector); void lapic_setup(void); int lapic_setup_clock(void); #endif /* !LOCORE */ #endif /* _MACHINE_APICVAR_H_ */ Index: head/sys/i386/include/frame.h =================================================================== --- head/sys/i386/include/frame.h (revision 153145) +++ head/sys/i386/include/frame.h (revision 153146) @@ -1,155 +1,125 @@ /*- * Copyright (c) 1990 The Regents of the University of California. * All rights reserved. * * This code is derived from software contributed to Berkeley by * William Jolitz. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * from: @(#)frame.h 5.2 (Berkeley) 1/18/91 * $FreeBSD$ */ #ifndef _MACHINE_FRAME_H_ #define _MACHINE_FRAME_H_ 1 /* * System stack frames. */ /* * Exception/Trap Stack Frame */ struct trapframe { int tf_fs; int tf_es; int tf_ds; int tf_edi; int tf_esi; int tf_ebp; int tf_isp; int tf_ebx; int tf_edx; int tf_ecx; int tf_eax; int tf_trapno; /* below portion defined in 386 hardware */ int tf_err; int tf_eip; int tf_cs; int tf_eflags; /* below only when crossing rings (e.g. user to kernel) */ int tf_esp; int tf_ss; }; /* Superset of trap frame, for traps from virtual-8086 mode */ struct trapframe_vm86 { int tf_fs; int tf_es; int tf_ds; int tf_edi; int tf_esi; int tf_ebp; int tf_isp; int tf_ebx; int tf_edx; int tf_ecx; int tf_eax; int tf_trapno; /* below portion defined in 386 hardware */ int tf_err; int tf_eip; int tf_cs; int tf_eflags; /* below only when crossing rings (e.g. user to kernel) */ int tf_esp; int tf_ss; /* below only when switching out of VM86 mode */ int tf_vm86_es; int tf_vm86_ds; int tf_vm86_fs; int tf_vm86_gs; }; -/* Interrupt stack frame */ +/* frame of clock (same as trap frame) */ -struct intrframe { - int if_vec; - int if_fs; - int if_es; - int if_ds; - int if_edi; - int if_esi; - int if_ebp; - int :32; - int if_ebx; - int if_edx; - int if_ecx; - int if_eax; - int :32; /* for compat with trap frame - trapno */ - int :32; /* for compat with trap frame - err */ - /* below portion defined in 386 hardware */ - int if_eip; - int if_cs; - int if_eflags; - /* below only when crossing rings (e.g. user to kernel) */ - int if_esp; - int if_ss; -}; - -/* frame of clock (same as interrupt frame) */ - struct clockframe { - int cf_vec; int cf_fs; int cf_es; int cf_ds; int cf_edi; int cf_esi; int cf_ebp; int :32; int cf_ebx; int cf_edx; int cf_ecx; int cf_eax; int :32; /* for compat with trap frame - trapno */ int :32; /* for compat with trap frame - err */ /* below portion defined in 386 hardware */ int cf_eip; int cf_cs; int cf_eflags; /* below only when crossing rings (e.g. user to kernel) */ int cf_esp; int cf_ss; }; - -#define CLOCK_TO_TRAPFRAME(frame) ((struct trapframe *)&(frame)->cf_fs) -#define INTR_TO_TRAPFRAME(frame) ((struct trapframe *)&(frame)->if_fs) #endif /* _MACHINE_FRAME_H_ */ Index: head/sys/i386/include/intr_machdep.h =================================================================== --- head/sys/i386/include/intr_machdep.h (revision 153145) +++ head/sys/i386/include/intr_machdep.h (revision 153146) @@ -1,134 +1,134 @@ /*- * Copyright (c) 2003 John Baldwin * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #ifndef __MACHINE_INTR_MACHDEP_H__ #define __MACHINE_INTR_MACHDEP_H__ #ifdef _KERNEL /* * The maximum number of I/O interrupts we allow. This number is rather * arbitrary as it is just the maximum IRQ resource value. The interrupt * source for a given IRQ maps that I/O interrupt to device interrupt * source whether it be a pin on an interrupt controller or an MSI interrupt. * The 16 ISA IRQs are assigned fixed IDT vectors, but all other device * interrupts allocate IDT vectors on demand. Currently we have 191 IDT * vectors available for device interrupts. On many systems with I/O APICs, * a lot of the IRQs are not used, so this number can be much larger than * 191 and still be safe since only interrupt sources in actual use will * allocate IDT vectors. * * For now we stick with 255 as ISA IRQs and PCI intline IRQs only allow * for IRQs in the range 0 - 254. When MSI support is added this number * will likely increase. */ #define NUM_IO_INTS 255 /* * - 1 ??? dummy counter. * - 2 counters for each I/O interrupt. * - 1 counter for each CPU for lapic timer. * - 7 counters for each CPU for IPI counters for SMP. */ #ifdef SMP #define INTRCNT_COUNT (1 + NUM_IO_INTS * 2 + 1) #else #define INTRCNT_COUNT (1 + NUM_IO_INTS * 2 + (1 + 7) * MAXCPU) #endif #ifndef LOCORE typedef void inthand_t(u_int cs, u_int ef, u_int esp, u_int ss); #define IDTVEC(name) __CONCAT(X,name) struct intsrc; /* * Methods that a PIC provides to mask/unmask a given interrupt source, * "turn on" the interrupt on the CPU side by setting up an IDT entry, and * return the vector associated with this source. */ struct pic { void (*pic_enable_source)(struct intsrc *); void (*pic_disable_source)(struct intsrc *, int); void (*pic_eoi_source)(struct intsrc *); void (*pic_enable_intr)(struct intsrc *); int (*pic_vector)(struct intsrc *); int (*pic_source_pending)(struct intsrc *); void (*pic_suspend)(struct intsrc *); void (*pic_resume)(struct intsrc *); int (*pic_config_intr)(struct intsrc *, enum intr_trigger, enum intr_polarity); }; /* Flags for pic_disable_source() */ enum { PIC_EOI, PIC_NO_EOI, }; /* * An interrupt source. The upper-layer code uses the PIC methods to * control a given source. The lower-layer PIC drivers can store additional * private data in a given interrupt source such as an interrupt pin number * or an I/O APIC pointer. */ struct intsrc { struct pic *is_pic; struct intr_event *is_event; u_long *is_count; u_long *is_straycount; u_int is_index; }; -struct intrframe; +struct trapframe; extern struct mtx icu_lock; extern int elcr_found; /* XXX: The elcr_* prototypes probably belong somewhere else. */ int elcr_probe(void); enum intr_trigger elcr_read_trigger(u_int irq); void elcr_resume(void); void elcr_write_trigger(u_int irq, enum intr_trigger trigger); int intr_add_handler(const char *name, int vector, driver_intr_t handler, void *arg, enum intr_type flags, void **cookiep); int intr_config_intr(int vector, enum intr_trigger trig, enum intr_polarity pol); -void intr_execute_handlers(struct intsrc *isrc, struct intrframe *iframe); +void intr_execute_handlers(struct intsrc *isrc, struct trapframe *frame); struct intsrc *intr_lookup_source(int vector); int intr_register_source(struct intsrc *isrc); int intr_remove_handler(void *cookie); void intr_resume(void); void intr_suspend(void); void intrcnt_add(const char *name, u_long **countp); #endif /* !LOCORE */ #endif /* _KERNEL */ #endif /* !__MACHINE_INTR_MACHDEP_H__ */ Index: head/sys/i386/isa/atpic.c =================================================================== --- head/sys/i386/isa/atpic.c (revision 153145) +++ head/sys/i386/isa/atpic.c (revision 153146) @@ -1,657 +1,656 @@ /*- * Copyright (c) 2003 John Baldwin * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the author nor the names of any co-contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * PIC driver for the 8259A Master and Slave PICs in PC/AT machines. */ #include __FBSDID("$FreeBSD$"); #include "opt_auto_eoi.h" #include "opt_isa.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef PC98 #include #else #include #endif #include #define MASTER 0 #define SLAVE 1 /* * PC-98 machines wire the slave 8259A to pin 7 on the master PIC, and * PC-AT machines wire the slave PIC to pin 2 on the master PIC. */ #ifdef PC98 #define ICU_SLAVEID 7 #else #define ICU_SLAVEID 2 #endif /* * Determine the base master and slave modes not including auto EOI support. * All machines that FreeBSD supports use 8086 mode. */ #ifdef PC98 /* * PC-98 machines do not support auto EOI on the second PIC. Also, it * seems that PC-98 machine PICs use buffered mode, and the master PIC * uses special fully nested mode. */ #define BASE_MASTER_MODE (ICW4_SFNM | ICW4_BUF | ICW4_MS | ICW4_8086) #define BASE_SLAVE_MODE (ICW4_BUF | ICW4_8086) #else #define BASE_MASTER_MODE ICW4_8086 #define BASE_SLAVE_MODE ICW4_8086 #endif /* Enable automatic EOI if requested. */ #ifdef AUTO_EOI_1 #define MASTER_MODE (BASE_MASTER_MODE | ICW4_AEOI) #else #define MASTER_MODE BASE_MASTER_MODE #endif #ifdef AUTO_EOI_2 #define SLAVE_MODE (BASE_SLAVE_MODE | ICW4_AEOI) #else #define SLAVE_MODE BASE_SLAVE_MODE #endif #define IRQ_MASK(irq) (1 << (irq)) #define IMEN_MASK(ai) (IRQ_MASK((ai)->at_irq)) #define NUM_ISA_IRQS 16 static void atpic_init(void *dummy); unsigned int imen; /* XXX */ inthand_t IDTVEC(atpic_intr0), IDTVEC(atpic_intr1), IDTVEC(atpic_intr2), IDTVEC(atpic_intr3), IDTVEC(atpic_intr4), IDTVEC(atpic_intr5), IDTVEC(atpic_intr6), IDTVEC(atpic_intr7), IDTVEC(atpic_intr8), IDTVEC(atpic_intr9), IDTVEC(atpic_intr10), IDTVEC(atpic_intr11), IDTVEC(atpic_intr12), IDTVEC(atpic_intr13), IDTVEC(atpic_intr14), IDTVEC(atpic_intr15); #define IRQ(ap, ai) ((ap)->at_irqbase + (ai)->at_irq) #define ATPIC(io, base, eoi, imenptr) \ { { atpic_enable_source, atpic_disable_source, (eoi), \ atpic_enable_intr, atpic_vector, atpic_source_pending, NULL, \ atpic_resume, atpic_config_intr }, (io), (base), \ IDT_IO_INTS + (base), (imenptr) } #define INTSRC(irq) \ { { &atpics[(irq) / 8].at_pic }, IDTVEC(atpic_intr ## irq ), \ (irq) % 8 } struct atpic { struct pic at_pic; int at_ioaddr; int at_irqbase; uint8_t at_intbase; uint8_t *at_imen; }; struct atpic_intsrc { struct intsrc at_intsrc; inthand_t *at_intr; int at_irq; /* Relative to PIC base. */ enum intr_trigger at_trigger; u_long at_count; u_long at_straycount; }; static void atpic_enable_source(struct intsrc *isrc); static void atpic_disable_source(struct intsrc *isrc, int eoi); static void atpic_eoi_master(struct intsrc *isrc); static void atpic_eoi_slave(struct intsrc *isrc); static void atpic_enable_intr(struct intsrc *isrc); static int atpic_vector(struct intsrc *isrc); static void atpic_resume(struct intsrc *isrc); static int atpic_source_pending(struct intsrc *isrc); static int atpic_config_intr(struct intsrc *isrc, enum intr_trigger trig, enum intr_polarity pol); static void i8259_init(struct atpic *pic, int slave); static struct atpic atpics[] = { ATPIC(IO_ICU1, 0, atpic_eoi_master, (uint8_t *)&imen), ATPIC(IO_ICU2, 8, atpic_eoi_slave, ((uint8_t *)&imen) + 1) }; static struct atpic_intsrc atintrs[] = { INTSRC(0), INTSRC(1), INTSRC(2), INTSRC(3), INTSRC(4), INTSRC(5), INTSRC(6), INTSRC(7), INTSRC(8), INTSRC(9), INTSRC(10), INTSRC(11), INTSRC(12), INTSRC(13), INTSRC(14), INTSRC(15), }; CTASSERT(sizeof(atintrs) / sizeof(atintrs[0]) == NUM_ISA_IRQS); static __inline void _atpic_eoi_master(struct intsrc *isrc) { KASSERT(isrc->is_pic == &atpics[MASTER].at_pic, ("%s: mismatched pic", __func__)); #ifndef AUTO_EOI_1 outb(atpics[MASTER].at_ioaddr, OCW2_EOI); #endif } /* * The data sheet says no auto-EOI on slave, but it sometimes works. * So, if AUTO_EOI_2 is enabled, we use it. */ static __inline void _atpic_eoi_slave(struct intsrc *isrc) { KASSERT(isrc->is_pic == &atpics[SLAVE].at_pic, ("%s: mismatched pic", __func__)); #ifndef AUTO_EOI_2 outb(atpics[SLAVE].at_ioaddr, OCW2_EOI); #ifndef AUTO_EOI_1 outb(atpics[MASTER].at_ioaddr, OCW2_EOI); #endif #endif } static void atpic_enable_source(struct intsrc *isrc) { struct atpic_intsrc *ai = (struct atpic_intsrc *)isrc; struct atpic *ap = (struct atpic *)isrc->is_pic; mtx_lock_spin(&icu_lock); if (*ap->at_imen & IMEN_MASK(ai)) { *ap->at_imen &= ~IMEN_MASK(ai); outb(ap->at_ioaddr + ICU_IMR_OFFSET, *ap->at_imen); } mtx_unlock_spin(&icu_lock); } static void atpic_disable_source(struct intsrc *isrc, int eoi) { struct atpic_intsrc *ai = (struct atpic_intsrc *)isrc; struct atpic *ap = (struct atpic *)isrc->is_pic; mtx_lock_spin(&icu_lock); if (ai->at_trigger != INTR_TRIGGER_EDGE) { *ap->at_imen |= IMEN_MASK(ai); outb(ap->at_ioaddr + ICU_IMR_OFFSET, *ap->at_imen); } /* * Take care to call these functions directly instead of through * a function pointer. All of the referenced variables should * still be hot in the cache. */ if (eoi == PIC_EOI) { if (isrc->is_pic == &atpics[MASTER].at_pic) _atpic_eoi_master(isrc); else _atpic_eoi_slave(isrc); } mtx_unlock_spin(&icu_lock); } static void atpic_eoi_master(struct intsrc *isrc) { #ifndef AUTO_EOI_1 mtx_lock_spin(&icu_lock); _atpic_eoi_master(isrc); mtx_unlock_spin(&icu_lock); #endif } static void atpic_eoi_slave(struct intsrc *isrc) { #ifndef AUTO_EOI_2 mtx_lock_spin(&icu_lock); _atpic_eoi_slave(isrc); mtx_unlock_spin(&icu_lock); #endif } static void atpic_enable_intr(struct intsrc *isrc) { } static int atpic_vector(struct intsrc *isrc) { struct atpic_intsrc *ai = (struct atpic_intsrc *)isrc; struct atpic *ap = (struct atpic *)isrc->is_pic; return (IRQ(ap, ai)); } static int atpic_source_pending(struct intsrc *isrc) { struct atpic_intsrc *ai = (struct atpic_intsrc *)isrc; struct atpic *ap = (struct atpic *)isrc->is_pic; return (inb(ap->at_ioaddr) & IMEN_MASK(ai)); } static void atpic_resume(struct intsrc *isrc) { struct atpic_intsrc *ai = (struct atpic_intsrc *)isrc; struct atpic *ap = (struct atpic *)isrc->is_pic; if (ai->at_irq == 0) { i8259_init(ap, ap == &atpics[SLAVE]); #ifndef PC98 if (ap == &atpics[SLAVE] && elcr_found) elcr_resume(); #endif } } static int atpic_config_intr(struct intsrc *isrc, enum intr_trigger trig, enum intr_polarity pol) { struct atpic_intsrc *ai = (struct atpic_intsrc *)isrc; u_int vector; /* Map conforming values to edge/hi and sanity check the values. */ if (trig == INTR_TRIGGER_CONFORM) trig = INTR_TRIGGER_EDGE; if (pol == INTR_POLARITY_CONFORM) pol = INTR_POLARITY_HIGH; vector = atpic_vector(isrc); if ((trig == INTR_TRIGGER_EDGE && pol == INTR_POLARITY_LOW) || (trig == INTR_TRIGGER_LEVEL && pol == INTR_POLARITY_HIGH)) { printf( "atpic: Mismatched config for IRQ%u: trigger %s, polarity %s\n", vector, trig == INTR_TRIGGER_EDGE ? "edge" : "level", pol == INTR_POLARITY_HIGH ? "high" : "low"); return (EINVAL); } /* If there is no change, just return. */ if (ai->at_trigger == trig) return (0); #ifdef PC98 if ((vector == 0 || vector == 1 || vector == 7 || vector == 8) && trig == INTR_TRIGGER_LEVEL) { if (bootverbose) printf( "atpic: Ignoring invalid level/low configuration for IRQ%u\n", vector); return (EINVAL); } return (ENXIO); #else /* * Certain IRQs can never be level/lo, so don't try to set them * that way if asked. At least some ELCR registers ignore setting * these bits as well. */ if ((vector == 0 || vector == 1 || vector == 2 || vector == 13) && trig == INTR_TRIGGER_LEVEL) { if (bootverbose) printf( "atpic: Ignoring invalid level/low configuration for IRQ%u\n", vector); return (EINVAL); } if (!elcr_found) { if (bootverbose) printf("atpic: No ELCR to configure IRQ%u as %s\n", vector, trig == INTR_TRIGGER_EDGE ? "edge/high" : "level/low"); return (ENXIO); } if (bootverbose) printf("atpic: Programming IRQ%u as %s\n", vector, trig == INTR_TRIGGER_EDGE ? "edge/high" : "level/low"); mtx_lock_spin(&icu_lock); elcr_write_trigger(atpic_vector(isrc), trig); ai->at_trigger = trig; mtx_unlock_spin(&icu_lock); return (0); #endif /* PC98 */ } static void i8259_init(struct atpic *pic, int slave) { int imr_addr; /* Reset the PIC and program with next four bytes. */ mtx_lock_spin(&icu_lock); #ifdef DEV_MCA /* MCA uses level triggered interrupts. */ if (MCA_system) outb(pic->at_ioaddr, ICW1_RESET | ICW1_IC4 | ICW1_LTIM); else #endif outb(pic->at_ioaddr, ICW1_RESET | ICW1_IC4); imr_addr = pic->at_ioaddr + ICU_IMR_OFFSET; /* Start vector. */ outb(imr_addr, pic->at_intbase); /* * Setup slave links. For the master pic, indicate what line * the slave is configured on. For the slave indicate * which line on the master we are connected to. */ if (slave) outb(imr_addr, ICU_SLAVEID); else outb(imr_addr, IRQ_MASK(ICU_SLAVEID)); /* Set mode. */ if (slave) outb(imr_addr, SLAVE_MODE); else outb(imr_addr, MASTER_MODE); /* Set interrupt enable mask. */ outb(imr_addr, *pic->at_imen); /* Reset is finished, default to IRR on read. */ outb(pic->at_ioaddr, OCW3_SEL | OCW3_RR); #ifndef PC98 /* OCW2_L1 sets priority order to 3-7, 0-2 (com2 first). */ if (!slave) outb(pic->at_ioaddr, OCW2_R | OCW2_SL | OCW2_L1); #endif mtx_unlock_spin(&icu_lock); } void atpic_startup(void) { struct atpic_intsrc *ai; int i; /* Start off with all interrupts disabled. */ imen = 0xffff; i8259_init(&atpics[MASTER], 0); i8259_init(&atpics[SLAVE], 1); atpic_enable_source((struct intsrc *)&atintrs[ICU_SLAVEID]); /* Install low-level interrupt handlers for all of our IRQs. */ for (i = 0, ai = atintrs; i < NUM_ISA_IRQS; i++, ai++) { if (i == ICU_SLAVEID) continue; ai->at_intsrc.is_count = &ai->at_count; ai->at_intsrc.is_straycount = &ai->at_straycount; setidt(((struct atpic *)ai->at_intsrc.is_pic)->at_intbase + ai->at_irq, ai->at_intr, SDT_SYS386IGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); } #ifdef DEV_MCA /* For MCA systems, all interrupts are level triggered. */ if (MCA_system) for (i = 0, ai = atintrs; i < NUM_ISA_IRQS; i++, ai++) ai->at_trigger = INTR_TRIGGER_LEVEL; else #endif #ifdef PC98 for (i = 0, ai = atintrs; i < NUM_ISA_IRQS; i++, ai++) switch (i) { case 0: case 1: case 7: case 8: ai->at_trigger = INTR_TRIGGER_EDGE; break; default: ai->at_trigger = INTR_TRIGGER_LEVEL; break; } #else /* * Look for an ELCR. If we find one, update the trigger modes. * If we don't find one, assume that IRQs 0, 1, 2, and 13 are * edge triggered and that everything else is level triggered. * We only use the trigger information to reprogram the ELCR if * we have one and as an optimization to avoid masking edge * triggered interrupts. For the case that we don't have an ELCR, * it doesn't hurt to mask an edge triggered interrupt, so we * assume level trigger for any interrupt that we aren't sure is * edge triggered. */ if (elcr_found) { for (i = 0, ai = atintrs; i < NUM_ISA_IRQS; i++, ai++) ai->at_trigger = elcr_read_trigger(i); } else { for (i = 0, ai = atintrs; i < NUM_ISA_IRQS; i++, ai++) switch (i) { case 0: case 1: case 2: case 8: case 13: ai->at_trigger = INTR_TRIGGER_EDGE; break; default: ai->at_trigger = INTR_TRIGGER_LEVEL; break; } } #endif /* PC98 */ } static void atpic_init(void *dummy __unused) { struct atpic_intsrc *ai; int i; /* * If any of the ISA IRQs have an interrupt source already, then * assume that the APICs are being used and don't register any * of our interrupt sources. This makes sure we don't accidentally * use mixed mode. The "accidental" use could otherwise occur on * machines that route the ACPI SCI interrupt to a different ISA * IRQ (at least one machines routes it to IRQ 13) thus disabling * that APIC ISA routing and allowing the ATPIC source for that IRQ * to leak through. We used to depend on this feature for routing * IRQ0 via mixed mode, but now we don't use mixed mode at all. */ for (i = 0; i < NUM_ISA_IRQS; i++) if (intr_lookup_source(i) != NULL) return; /* Loop through all interrupt sources and add them. */ for (i = 0, ai = atintrs; i < NUM_ISA_IRQS; i++, ai++) { if (i == ICU_SLAVEID) continue; intr_register_source(&ai->at_intsrc); } } SYSINIT(atpic_init, SI_SUB_INTR, SI_ORDER_SECOND + 1, atpic_init, NULL) void -atpic_handle_intr(struct intrframe iframe) +atpic_handle_intr(u_int vector, struct trapframe frame) { struct intsrc *isrc; - KASSERT((u_int)iframe.if_vec < NUM_ISA_IRQS, - ("unknown int %d\n", iframe.if_vec)); - isrc = &atintrs[iframe.if_vec].at_intsrc; + KASSERT(vector < NUM_ISA_IRQS, + ("unknown int %u\n", vector)); + isrc = &atintrs[vector].at_intsrc; /* * If we don't have an event, see if this is a spurious * interrupt. */ - if (isrc->is_event == NULL && - (iframe.if_vec == 7 || iframe.if_vec == 15)) { + if (isrc->is_event == NULL && (vector == 7 || vector == 15)) { int port, isr; /* * Read the ISR register to see if IRQ 7/15 is really * pending. Reset read register back to IRR when done. */ port = ((struct atpic *)isrc->is_pic)->at_ioaddr; mtx_lock_spin(&icu_lock); outb(port, OCW3_SEL | OCW3_RR | OCW3_RIS); isr = inb(port); outb(port, OCW3_SEL | OCW3_RR); mtx_unlock_spin(&icu_lock); if ((isr & IRQ_MASK(7)) == 0) return; } - intr_execute_handlers(isrc, &iframe); + intr_execute_handlers(isrc, &frame); } #ifdef DEV_ISA /* * Bus attachment for the ISA PIC. */ static struct isa_pnp_id atpic_ids[] = { { 0x0000d041 /* PNP0000 */, "AT interrupt controller" }, { 0 } }; static int atpic_probe(device_t dev) { int result; result = ISA_PNP_PROBE(device_get_parent(dev), dev, atpic_ids); if (result <= 0) device_quiet(dev); return (result); } /* * We might be granted IRQ 2, as this is typically consumed by chaining * between the two PIC components. If we're using the APIC, however, * this may not be the case, and as such we should free the resource. * (XXX untested) * * The generic ISA attachment code will handle allocating any other resources * that we don't explicitly claim here. */ static int atpic_attach(device_t dev) { struct resource *res; int rid; /* Try to allocate our IRQ and then free it. */ rid = 0; res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, 0); if (res != NULL) bus_release_resource(dev, SYS_RES_IRQ, rid, res); return (0); } static device_method_t atpic_methods[] = { /* Device interface */ DEVMETHOD(device_probe, atpic_probe), DEVMETHOD(device_attach, atpic_attach), DEVMETHOD(device_detach, bus_generic_detach), DEVMETHOD(device_shutdown, bus_generic_shutdown), DEVMETHOD(device_suspend, bus_generic_suspend), DEVMETHOD(device_resume, bus_generic_resume), { 0, 0 } }; static driver_t atpic_driver = { "atpic", atpic_methods, 1, /* no softc */ }; static devclass_t atpic_devclass; DRIVER_MODULE(atpic, isa, atpic_driver, atpic_devclass, 0, 0); #ifndef PC98 DRIVER_MODULE(atpic, acpi, atpic_driver, atpic_devclass, 0, 0); #endif /* * Return a bitmap of the current interrupt requests. This is 8259-specific * and is only suitable for use at probe time. */ intrmask_t isa_irq_pending(void) { u_char irr1; u_char irr2; irr1 = inb(IO_ICU1); irr2 = inb(IO_ICU2); return ((irr2 << 8) | irr1); } #endif /* DEV_ISA */ Index: head/sys/i386/isa/icu.h =================================================================== --- head/sys/i386/isa/icu.h (revision 153145) +++ head/sys/i386/isa/icu.h (revision 153146) @@ -1,53 +1,53 @@ /*- * Copyright (c) 1990 The Regents of the University of California. * All rights reserved. * * This code is derived from software contributed to Berkeley by * William Jolitz. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * from: @(#)icu.h 5.6 (Berkeley) 5/9/91 * $FreeBSD$ */ /* * AT/386 Interrupt Control constants * W. Jolitz 8/89 */ #ifndef _I386_ISA_ICU_H_ #define _I386_ISA_ICU_H_ #ifdef PC98 #define ICU_IMR_OFFSET 2 #else #define ICU_IMR_OFFSET 1 #endif -void atpic_handle_intr(struct intrframe iframe); +void atpic_handle_intr(u_int vector, struct trapframe frame); void atpic_startup(void); #endif /* !_I386_ISA_ICU_H_ */