📄 apic.c
字号:
smp_found_config = 0; skip_ioapic_setup = 1; goto fake_ioapic_page; } } else {fake_ioapic_page: ioapic_phys = __pa(alloc_xenheap_page()); clear_page(__va(ioapic_phys)); } set_fixmap_nocache(idx, ioapic_phys); apic_printk(APIC_VERBOSE, "mapped IOAPIC to %08lx (%08lx)\n", __fix_to_virt(idx), ioapic_phys); idx++; } }#endif}/***************************************************************************** * APIC calibration * * The APIC is programmed in bus cycles. * Timeout values should specified in real time units. * The "cheapest" time source is the cyclecounter. * * Thus, we need a mappings from: bus cycles <- cycle counter <- system time * * The calibration is currently a bit shoddy since it requires the external * timer chip to generate periodic timer interupts. *****************************************************************************//* used for system time scaling */static unsigned long bus_freq; /* KAF: pointer-size avoids compile warns. */static u32 bus_cycle; /* length of one bus cycle in pico-seconds */static u32 bus_scale; /* scaling factor convert ns to bus cycles *//* * The timer chip is already set up at HZ interrupts per second here, * but we do not accept timer interrupts yet. We only allow the BP * to calibrate. */static unsigned int __init get_8254_timer_count(void){ /*extern spinlock_t i8253_lock;*/ /*unsigned long flags;*/ unsigned int count; /*spin_lock_irqsave(&i8253_lock, flags);*/ outb_p(0x00, PIT_MODE); count = inb_p(PIT_CH0); count |= inb_p(PIT_CH0) << 8; /*spin_unlock_irqrestore(&i8253_lock, flags);*/ return count;}/* next tick in 8254 can be caught by catching timer wraparound */static void __init wait_8254_wraparound(void){ unsigned int curr_count, prev_count; curr_count = get_8254_timer_count(); do { prev_count = curr_count; curr_count = get_8254_timer_count(); /* workaround for broken Mercury/Neptune */ if (prev_count >= curr_count + 0x100) curr_count = get_8254_timer_count(); } while (prev_count >= curr_count);}/* * Default initialization for 8254 timers. If we use other timers like HPET, * we override this later */void (*wait_timer_tick)(void) __initdata = wait_8254_wraparound;/* * This function sets up the local APIC timer, with a timeout of * 'clocks' APIC bus clock. During calibration we actually call * this function twice on the boot CPU, once with a bogus timeout * value, second time for real. The other (noncalibrating) CPUs * call this function only once, with the real, calibrated value. * * We do reads before writes even if unnecessary, to get around the * P5 APIC double write bug. */#define APIC_DIVISOR 1void __setup_APIC_LVTT(unsigned int clocks){ unsigned int lvtt_value, tmp_value, ver; ver = GET_APIC_VERSION(apic_read(APIC_LVR)); /* NB. Xen uses local APIC timer in one-shot mode. */ lvtt_value = /*APIC_LVT_TIMER_PERIODIC |*/ LOCAL_TIMER_VECTOR; if (!APIC_INTEGRATED(ver)) lvtt_value |= SET_APIC_TIMER_BASE(APIC_TIMER_BASE_DIV); apic_write_around(APIC_LVTT, lvtt_value); tmp_value = apic_read(APIC_TDCR); apic_write_around(APIC_TDCR, (tmp_value | APIC_TDR_DIV_1)); apic_write_around(APIC_TMICT, clocks/APIC_DIVISOR);}static void __devinit setup_APIC_timer(unsigned int clocks){ unsigned long flags; local_irq_save(flags); __setup_APIC_LVTT(clocks); local_irq_restore(flags);}/* * In this function we calibrate APIC bus clocks to the external * timer. Unfortunately we cannot use jiffies and the timer irq * to calibrate, since some later bootup code depends on getting * the first irq? Ugh. * * We want to do the calibration only once since we * want to have local timer irqs syncron. CPUs connected * by the same APIC bus have the very same bus frequency. * And we want to have irqs off anyways, no accidental * APIC irq that way. */int __init calibrate_APIC_clock(void){ unsigned long long t1 = 0, t2 = 0; long tt1, tt2; long result; int i; const int LOOPS = HZ/10; apic_printk(APIC_VERBOSE, "calibrating APIC timer ...\n"); /* * Put whatever arbitrary (but long enough) timeout * value into the APIC clock, we just want to get the * counter running for calibration. */ __setup_APIC_LVTT(1000000000); /* * The timer chip counts down to zero. Let's wait * for a wraparound to start exact measurement: * (the current tick might have been already half done) */ wait_timer_tick(); /* * We wrapped around just now. Let's start: */ if (cpu_has_tsc) rdtscll(t1); tt1 = apic_read(APIC_TMCCT); /* * Let's wait LOOPS wraprounds: */ for (i = 0; i < LOOPS; i++) wait_timer_tick(); tt2 = apic_read(APIC_TMCCT); if (cpu_has_tsc) rdtscll(t2); /* * The APIC bus clock counter is 32 bits only, it * might have overflown, but note that we use signed * longs, thus no extra care needed. * * underflown to be exact, as the timer counts down ;) */ result = (tt1-tt2)*APIC_DIVISOR/LOOPS; if (cpu_has_tsc) apic_printk(APIC_VERBOSE, "..... CPU clock speed is " "%ld.%04ld MHz.\n", ((long)(t2-t1)/LOOPS)/(1000000/HZ), ((long)(t2-t1)/LOOPS)%(1000000/HZ)); apic_printk(APIC_VERBOSE, "..... host bus clock speed is " "%ld.%04ld MHz.\n", result/(1000000/HZ), result%(1000000/HZ)); /* set up multipliers for accurate timer code */ bus_freq = result*HZ; bus_cycle = (u32) (1000000000000LL/bus_freq); /* in pico seconds */ bus_scale = (1000*262144)/bus_cycle; apic_printk(APIC_VERBOSE, "..... bus_scale = 0x%08X\n", bus_scale); /* reset APIC to zero timeout value */ __setup_APIC_LVTT(0); return result;}u32 get_apic_bus_cycle(void){ return bus_cycle;}static unsigned int calibration_result;void __init setup_boot_APIC_clock(void){ unsigned long flags; apic_printk(APIC_VERBOSE, "Using local APIC timer interrupts.\n"); using_apic_timer = 1; local_irq_save(flags); calibration_result = calibrate_APIC_clock(); /* * Now set up the timer for real. */ setup_APIC_timer(calibration_result); local_irq_restore(flags);}void __devinit setup_secondary_APIC_clock(void){ setup_APIC_timer(calibration_result);}void disable_APIC_timer(void){ if (using_apic_timer) { unsigned long v; v = apic_read(APIC_LVTT); apic_write_around(APIC_LVTT, v | APIC_LVT_MASKED); }}void enable_APIC_timer(void){ if (using_apic_timer) { unsigned long v; v = apic_read(APIC_LVTT); apic_write_around(APIC_LVTT, v & ~APIC_LVT_MASKED); }}#undef APIC_DIVISOR/* * reprogram the APIC timer. Timeoutvalue is in ns from start of boot * returns 1 on success * returns 0 if the timeout value is too small or in the past. */int reprogram_timer(s_time_t timeout){ s_time_t now; s_time_t expire; u64 apic_tmict; /* * If we don't have local APIC then we just poll the timer list off the * PIT interrupt. */ if ( !cpu_has_apic ) return 1; /* * We use this value because we don't trust zero (we think it may just * cause an immediate interrupt). At least this is guaranteed to hold it * off for ages (esp. since the clock ticks on bus clock, not cpu clock!). */ if ( timeout == 0 ) { apic_tmict = 0xffffffff; goto reprogram; } now = NOW(); expire = timeout - now; /* value from now */ if ( expire <= 0 ) { Dprintk("APICT[%02d] Timeout in the past 0x%08X%08X > 0x%08X%08X\n", smp_processor_id(), (u32)(now>>32), (u32)now, (u32)(timeout>>32),(u32)timeout); return 0; } /* conversion to bus units */ apic_tmict = (((u64)bus_scale) * expire)>>18; if ( apic_tmict >= 0xffffffff ) { Dprintk("APICT[%02d] Timeout value too large\n", smp_processor_id()); apic_tmict = 0xffffffff; } if ( apic_tmict == 0 ) { Dprintk("APICT[%02d] timeout value too small\n", smp_processor_id()); return 0; } reprogram: /* Program the timer. */ apic_write(APIC_TMICT, (unsigned long)apic_tmict); return 1;}fastcall void smp_apic_timer_interrupt(struct cpu_user_regs * regs){ ack_APIC_irq(); perfc_incr(apic_timer); raise_softirq(TIMER_SOFTIRQ);}/* * This interrupt should _never_ happen with our APIC/SMP architecture */fastcall void smp_spurious_interrupt(struct cpu_user_regs *regs){ unsigned long v; irq_enter(); /* * Check if this really is a spurious interrupt and ACK it * if it is a vectored one. Just in case... * Spurious interrupts should not be ACKed. */ v = apic_read(APIC_ISR + ((SPURIOUS_APIC_VECTOR & ~0x1f) >> 1)); if (v & (1 << (SPURIOUS_APIC_VECTOR & 0x1f))) ack_APIC_irq(); /* see sw-dev-man vol 3, chapter 7.4.13.5 */ printk(KERN_INFO "spurious APIC interrupt on CPU#%d, should never happen.\n", smp_processor_id()); irq_exit();}/* * This interrupt should never happen with our APIC/SMP architecture */fastcall void smp_error_interrupt(struct cpu_user_regs *regs){ unsigned long v, v1; irq_enter(); /* First tickle the hardware, only then report what went on. -- REW */ v = apic_read(APIC_ESR); apic_write(APIC_ESR, 0); v1 = apic_read(APIC_ESR); ack_APIC_irq(); atomic_inc(&irq_err_count); /* Here is what the APIC error bits mean: 0: Send CS error 1: Receive CS error 2: Send accept error 3: Receive accept error 4: Reserved 5: Send illegal vector 6: Received illegal vector 7: Illegal register address */ printk (KERN_DEBUG "APIC error on CPU%d: %02lx(%02lx)\n", smp_processor_id(), v , v1); irq_exit();}/* * This interrupt handles performance counters interrupt */fastcall void smp_pmu_apic_interrupt(struct cpu_user_regs *regs){ ack_APIC_irq(); hvm_do_pmu_interrupt(regs);}/* * This initializes the IO-APIC and APIC hardware if this is * a UP kernel. */int __init APIC_init_uniprocessor (void){ if (enable_local_apic < 0) clear_bit(X86_FEATURE_APIC, boot_cpu_data.x86_capability); if (!smp_found_config && !cpu_has_apic) return -1; /* * Complain if the BIOS pretends there is one. */ if (!cpu_has_apic && APIC_INTEGRATED(apic_version[boot_cpu_physical_apicid])) { printk(KERN_ERR "BIOS bug, local APIC #%d not detected!...\n", boot_cpu_physical_apicid); clear_bit(X86_FEATURE_APIC, boot_cpu_data.x86_capability); return -1; } verify_local_APIC(); connect_bsp_APIC(); /* * Hack: In case of kdump, after a crash, kernel might be booting * on a cpu with non-zero lapic id. But boot_cpu_physical_apicid * might be zero if read from MP tables. Get it from LAPIC. */#ifdef CONFIG_CRASH_DUMP boot_cpu_physical_apicid = get_apic_id();#endif phys_cpu_present_map = physid_mask_of_physid(boot_cpu_physical_apicid); setup_local_APIC(); if (nmi_watchdog == NMI_LOCAL_APIC) check_nmi_watchdog();#ifdef CONFIG_X86_IO_APIC if (smp_found_config) if (!skip_ioapic_setup && nr_ioapics) setup_IO_APIC();#endif setup_boot_APIC_clock(); return 0;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -