void apic_attach(struct elroy_softc *sc) { volatile struct elroy_regs *r = sc->sc_regs; u_int32_t data; data = apic_read(r, APIC_VERSION); sc->sc_nints = (data & APIC_VERSION_NENT) >> APIC_VERSION_NENT_SHIFT; printf(" APIC ver %x, %d pins", data & APIC_VERSION_MASK, sc->sc_nints); sc->sc_irq = mallocarray(sc->sc_nints, sizeof(int), M_DEVBUF, M_NOWAIT | M_ZERO); if (sc->sc_irq == NULL) panic("apic_attach: cannot allocate irq table"); apic_get_int_tbl(sc); #ifdef DEBUG apic_dump(sc); #endif }
void apic_init (struct cpu * core) { struct apic_dev * apic = NULL; ulong_t base_addr; uint32_t val; apic = (struct apic_dev*)malloc(sizeof(struct apic_dev)); if (!apic) { panic("Could not allocate apic struct\n"); } memset(apic, 0, sizeof(struct apic_dev)); core->apic = apic; if (!check_apic_avail()) { panic("No APIC found on core %u, dying\n", core->id); } /* In response to AMD erratum #663 * the damn thing may give us lint interrupts * even when we have them masked */ if (nk_is_amd() && cpuid_get_family() == 0x15) { APIC_DEBUG("Writing Bridge Ctrl MSR for AMD Errata #663\n"); msr_write(AMD_MSR_NBRIDGE_CTL, msr_read(AMD_MSR_NBRIDGE_CTL) | (1ULL<<23) | (1ULL<<54)); } base_addr = apic_get_base_addr(); /* idempotent when not compiled as HRT */ apic->base_addr = pa_to_va(base_addr); #ifndef NAUT_CONFIG_HVM_HRT if (core->is_bsp) { /* map in the lapic as uncacheable */ if (nk_map_page_nocache(apic->base_addr, PTE_PRESENT_BIT|PTE_WRITABLE_BIT, PS_4K) == -1) { panic("Could not map APIC\n"); } } #endif apic->version = apic_get_version(apic); apic->id = apic_get_id(apic); #ifndef NAUT_CONFIG_XEON_PHI if (apic->version < 0x10 || apic->version > 0x15) { panic("Unsupported APIC version (0x%1x)\n", (unsigned)apic->version); } #endif val = apic_read(apic, APIC_REG_LDR) & ~APIC_LDR_MASK; val |= SET_APIC_LOGICAL_ID(0); apic_write(apic, APIC_REG_LDR, val); apic_write(apic, APIC_REG_TPR, apic_read(apic, APIC_REG_TPR) & 0xffffff00); // accept all interrupts apic_write(apic, APIC_REG_LVTT, APIC_DEL_MODE_FIXED | APIC_LVT_DISABLED); // disable timer interrupts intially apic_write(apic, APIC_REG_LVTPC, APIC_DEL_MODE_FIXED | APIC_LVT_DISABLED | APIC_PC_INT_VEC); // disable perf cntr interrupts apic_write(apic, APIC_REG_LVTTHMR, APIC_DEL_MODE_FIXED | APIC_LVT_DISABLED | APIC_THRML_INT_VEC); // disable thermal interrupts /* do we have AMD extended LVT entries to deal with */ if (nk_is_amd() && amd_has_ext_lvt(apic)) { amd_setup_ext_lvt(apic); } /* mask 8259a interrupts */ apic_write(apic, APIC_REG_LVT0, APIC_DEL_MODE_EXTINT | APIC_LVT_DISABLED); /* only BSP takes NMI interrupts */ apic_write(apic, APIC_REG_LVT1, APIC_DEL_MODE_NMI | (core->is_bsp ? 0 : APIC_LVT_DISABLED)); apic_write(apic, APIC_REG_LVTERR, APIC_DEL_MODE_FIXED | APIC_ERROR_INT_VEC); // allow error interrupts // clear the ESR apic_write(apic, APIC_REG_ESR, 0u); apic_global_enable(); // assign interrupt handlers if (core->is_bsp) { if (register_int_handler(APIC_NULL_KICK_VEC, null_kick, apic) != 0) { panic("Could not register null kick interrupt handler\n"); } if (register_int_handler(APIC_SPUR_INT_VEC, spur_int_handler, apic) != 0) { panic("Could not register spurious interrupt handler\n"); } if (register_int_handler(APIC_ERROR_INT_VEC, error_int_handler, apic) != 0) { panic("Could not register spurious interrupt handler\n"); return; } /* we shouldn't ever get these, but just in case */ if (register_int_handler(APIC_PC_INT_VEC, pc_int_handler, apic) != 0) { panic("Could not register perf counter interrupt handler\n"); return; } if (register_int_handler(APIC_THRML_INT_VEC, thermal_int_handler, apic) != 0) { panic("Could not register thermal interrupt handler\n"); return; } if (register_int_handler(APIC_EXT_LVT_DUMMY_VEC, dummy_int_handler, apic) != 0) { panic("Could not register dummy ext lvt handler\n"); return; } } apic_assign_spiv(apic, APIC_SPUR_INT_VEC); /* turn it on */ apic_sw_enable(apic); /* pass in quantum as milliseconds */ #ifndef NAUT_CONFIG_XEON_PHI apic_timer_setup(apic, 1000/NAUT_CONFIG_HZ); #endif apic_dump(apic); }
/* * Enable LAPIC, configure interrupts. */ void lapic_init(boolean_t bsp) { uint32_t timer; u_int temp; /* * Install vectors * * Since IDT is shared between BSP and APs, these vectors * only need to be installed once; we do it on BSP. */ if (bsp) { if (cpu_vendor_id == CPU_VENDOR_AMD && CPUID_TO_FAMILY(cpu_id) >= 0xf) { uint32_t tcr; /* * Set the LINTEN bit in the HyperTransport * Transaction Control Register. * * This will cause EXTINT and NMI interrupts * routed over the hypertransport bus to be * fed into the LAPIC LINT0/LINT1. If the bit * isn't set, the interrupts will go to the * general cpu INTR/NMI pins. On a dual-core * cpu the interrupt winds up going to BOTH cpus. * The first cpu that does the interrupt ack * cycle will get the correct interrupt. The * second cpu that does it will get a spurious * interrupt vector (typically IRQ 7). */ outl(0x0cf8, (1 << 31) | /* enable */ (0 << 16) | /* bus */ (0x18 << 11) | /* dev (cpu + 0x18) */ (0 << 8) | /* func */ 0x68 /* reg */ ); tcr = inl(0xcfc); if ((tcr & 0x00010000) == 0) { kprintf("LAPIC: AMD LINTEN on\n"); outl(0xcfc, tcr|0x00010000); } outl(0x0cf8, 0); } /* Install a 'Spurious INTerrupt' vector */ setidt_global(XSPURIOUSINT_OFFSET, Xspuriousint, SDT_SYSIGT, SEL_KPL, 0); /* Install a timer vector */ setidt_global(XTIMER_OFFSET, Xtimer, SDT_SYSIGT, SEL_KPL, 0); /* Install an inter-CPU IPI for TLB invalidation */ setidt_global(XINVLTLB_OFFSET, Xinvltlb, SDT_SYSIGT, SEL_KPL, 0); /* Install an inter-CPU IPI for IPIQ messaging */ setidt_global(XIPIQ_OFFSET, Xipiq, SDT_SYSIGT, SEL_KPL, 0); /* Install an inter-CPU IPI for CPU stop/restart */ setidt_global(XCPUSTOP_OFFSET, Xcpustop, SDT_SYSIGT, SEL_KPL, 0); } /* * Setup LINT0 as ExtINT on the BSP. This is theoretically an * aggregate interrupt input from the 8259. The INTA cycle * will be routed to the external controller (the 8259) which * is expected to supply the vector. * * Must be setup edge triggered, active high. * * Disable LINT0 on BSP, if I/O APIC is enabled. * * Disable LINT0 on the APs. It doesn't matter what delivery * mode we use because we leave it masked. */ temp = lapic->lvt_lint0; temp &= ~(APIC_LVT_MASKED | APIC_LVT_TRIG_MASK | APIC_LVT_POLARITY_MASK | APIC_LVT_DM_MASK); if (bsp) { temp |= APIC_LVT_DM_EXTINT; if (ioapic_enable) temp |= APIC_LVT_MASKED; } else { temp |= APIC_LVT_DM_FIXED | APIC_LVT_MASKED; } lapic->lvt_lint0 = temp; /* * Setup LINT1 as NMI. * * Must be setup edge trigger, active high. * * Enable LINT1 on BSP, if I/O APIC is enabled. * * Disable LINT1 on the APs. */ temp = lapic->lvt_lint1; temp &= ~(APIC_LVT_MASKED | APIC_LVT_TRIG_MASK | APIC_LVT_POLARITY_MASK | APIC_LVT_DM_MASK); temp |= APIC_LVT_MASKED | APIC_LVT_DM_NMI; if (bsp && ioapic_enable) temp &= ~APIC_LVT_MASKED; lapic->lvt_lint1 = temp; /* * Mask the LAPIC error interrupt, LAPIC performance counter * interrupt. */ lapic->lvt_error = lapic->lvt_error | APIC_LVT_MASKED; lapic->lvt_pcint = lapic->lvt_pcint | APIC_LVT_MASKED; /* * Set LAPIC timer vector and mask the LAPIC timer interrupt. */ timer = lapic->lvt_timer; timer &= ~APIC_LVTT_VECTOR; timer |= XTIMER_OFFSET; timer |= APIC_LVTT_MASKED; lapic->lvt_timer = timer; /* * Set the Task Priority Register as needed. At the moment allow * interrupts on all cpus (the APs will remain CLId until they are * ready to deal). */ temp = lapic->tpr; temp &= ~APIC_TPR_PRIO; /* clear priority field */ lapic->tpr = temp; /* * Enable the LAPIC */ temp = lapic->svr; temp |= APIC_SVR_ENABLE; /* enable the LAPIC */ temp &= ~APIC_SVR_FOCUS_DISABLE; /* enable lopri focus processor */ /* * Set the spurious interrupt vector. The low 4 bits of the vector * must be 1111. */ if ((XSPURIOUSINT_OFFSET & 0x0F) != 0x0F) panic("bad XSPURIOUSINT_OFFSET: 0x%08x", XSPURIOUSINT_OFFSET); temp &= ~APIC_SVR_VECTOR; temp |= XSPURIOUSINT_OFFSET; lapic->svr = temp; /* * Pump out a few EOIs to clean out interrupts that got through * before we were able to set the TPR. */ lapic->eoi = 0; lapic->eoi = 0; lapic->eoi = 0; if (bsp) { lapic_timer_calibrate(); if (lapic_timer_enable) { cputimer_intr_register(&lapic_cputimer_intr); cputimer_intr_select(&lapic_cputimer_intr, 0); } } else { lapic_timer_set_divisor(lapic_timer_divisor_idx); } if (bootverbose) apic_dump("apic_initialize()"); }