📄 apic.c
字号:
apic_write_around(APIC_LVT1, value); if (APIC_INTEGRATED(ver) && !esr_disable) { /* !82489DX */ maxlvt = get_maxlvt(); if (maxlvt > 3) /* Due to the Pentium erratum 3AP. */ apic_write(APIC_ESR, 0); value = apic_read(APIC_ESR); printk("ESR value before enabling vector: %08lx\n", value); value = ERROR_APIC_VECTOR; // enables sending errors apic_write_around(APIC_LVTERR, value); /* * spec says clear errors after enabling vector. */ if (maxlvt > 3) apic_write(APIC_ESR, 0); value = apic_read(APIC_ESR); printk("ESR value after enabling vector: %08lx\n", value); } else { if (esr_disable) /* * Something untraceble is creating bad interrupts on * secondary quads ... for the moment, just leave the * ESR disabled - we can't do anything useful with the * errors anyway - mbligh */ printk("Leaving ESR disabled.\n"); else printk("No ESR for 82489DX.\n"); } if (nmi_watchdog == NMI_LOCAL_APIC) setup_apic_nmi_watchdog();}#ifdef CONFIG_PM#include <linux/slab.h>#include <linux/pm.h>static struct { /* 'active' is true if the local APIC was enabled by us and not the BIOS; this signifies that we are also responsible for disabling it before entering apm/acpi suspend */ int active; /* 'perfctr_pmdev' is here because the current (2.4.1) PM callback system doesn't handle hierarchical dependencies */ struct pm_dev *perfctr_pmdev; /* r/w apic fields */ unsigned int apic_id; unsigned int apic_taskpri; unsigned int apic_ldr; unsigned int apic_dfr; unsigned int apic_spiv; unsigned int apic_lvtt; unsigned int apic_lvtpc; unsigned int apic_lvt0; unsigned int apic_lvt1; unsigned int apic_lvterr; unsigned int apic_tmict; unsigned int apic_tdcr;} apic_pm_state;static void apic_pm_suspend(void *data){ unsigned long flags; if (apic_pm_state.perfctr_pmdev) pm_send(apic_pm_state.perfctr_pmdev, PM_SUSPEND, data); apic_pm_state.apic_id = apic_read(APIC_ID); apic_pm_state.apic_taskpri = apic_read(APIC_TASKPRI); apic_pm_state.apic_ldr = apic_read(APIC_LDR); apic_pm_state.apic_dfr = apic_read(APIC_DFR); apic_pm_state.apic_spiv = apic_read(APIC_SPIV); apic_pm_state.apic_lvtt = apic_read(APIC_LVTT); apic_pm_state.apic_lvtpc = apic_read(APIC_LVTPC); apic_pm_state.apic_lvt0 = apic_read(APIC_LVT0); apic_pm_state.apic_lvt1 = apic_read(APIC_LVT1); apic_pm_state.apic_lvterr = apic_read(APIC_LVTERR); apic_pm_state.apic_tmict = apic_read(APIC_TMICT); apic_pm_state.apic_tdcr = apic_read(APIC_TDCR); __save_flags(flags); __cli(); disable_local_APIC(); __restore_flags(flags);}static void apic_pm_resume(void *data){ unsigned int l, h; unsigned long flags; __save_flags(flags); __cli(); /* * Make sure the APICBASE points to the right address * * FIXME! This will be wrong if we ever support suspend on * SMP! We'll need to do this as part of the CPU restore! */ rdmsr(MSR_IA32_APICBASE, l, h); l &= ~MSR_IA32_APICBASE_BASE; l |= MSR_IA32_APICBASE_ENABLE | mp_lapic_addr; wrmsr(MSR_IA32_APICBASE, l, h); apic_write(APIC_LVTERR, ERROR_APIC_VECTOR | APIC_LVT_MASKED); apic_write(APIC_ID, apic_pm_state.apic_id); apic_write(APIC_DFR, apic_pm_state.apic_dfr); apic_write(APIC_LDR, apic_pm_state.apic_ldr); apic_write(APIC_TASKPRI, apic_pm_state.apic_taskpri); apic_write(APIC_SPIV, apic_pm_state.apic_spiv); apic_write(APIC_LVT0, apic_pm_state.apic_lvt0); apic_write(APIC_LVT1, apic_pm_state.apic_lvt1); apic_write(APIC_LVTPC, apic_pm_state.apic_lvtpc); apic_write(APIC_LVTT, apic_pm_state.apic_lvtt); apic_write(APIC_TDCR, apic_pm_state.apic_tdcr); apic_write(APIC_TMICT, apic_pm_state.apic_tmict); apic_write(APIC_ESR, 0); apic_read(APIC_ESR); apic_write(APIC_LVTERR, apic_pm_state.apic_lvterr); apic_write(APIC_ESR, 0); apic_read(APIC_ESR); __restore_flags(flags); if (apic_pm_state.perfctr_pmdev) pm_send(apic_pm_state.perfctr_pmdev, PM_RESUME, data);}static int apic_pm_callback(struct pm_dev *dev, pm_request_t rqst, void *data){ switch (rqst) { case PM_SUSPEND: apic_pm_suspend(data); break; case PM_RESUME: apic_pm_resume(data); break; } return 0;}/* perfctr driver should call this instead of pm_register() */struct pm_dev *apic_pm_register(pm_dev_t type, unsigned long id, pm_callback callback){ struct pm_dev *dev; if (!apic_pm_state.active) return pm_register(type, id, callback); if (apic_pm_state.perfctr_pmdev) return NULL; /* we're busy */ dev = kmalloc(sizeof(struct pm_dev), GFP_KERNEL); if (dev) { memset(dev, 0, sizeof(*dev)); dev->type = type; dev->id = id; dev->callback = callback; apic_pm_state.perfctr_pmdev = dev; } return dev;}/* perfctr driver should call this instead of pm_unregister() */void apic_pm_unregister(struct pm_dev *dev){ if (!apic_pm_state.active) { pm_unregister(dev); } else if (dev == apic_pm_state.perfctr_pmdev) { apic_pm_state.perfctr_pmdev = NULL; kfree(dev); }}static void __init apic_pm_init1(void){ /* can't pm_register() at this early stage in the boot process (causes an immediate reboot), so just set the flag */ apic_pm_state.active = 1;}static void __init apic_pm_init2(void){ if (apic_pm_state.active) pm_register(PM_SYS_DEV, 0, apic_pm_callback);}#else /* CONFIG_PM */static inline void apic_pm_init1(void) { }static inline void apic_pm_init2(void) { }#endif /* CONFIG_PM *//* * Detect and enable local APICs on non-SMP boards. * Original code written by Keir Fraser. *//* * Knob to control our willingness to enable the local APIC. */int enable_local_apic __initdata = 0; /* -1=force-disable, +1=force-enable */static int __init lapic_disable(char *str){ enable_local_apic = -1; clear_bit(X86_FEATURE_APIC, boot_cpu_data.x86_capability); return 0;}__setup("nolapic", lapic_disable);static int __init lapic_enable(char *str){ enable_local_apic = 1; return 0;}__setup("lapic", lapic_enable);static int __init detect_init_APIC (void){ u32 h, l, features; extern void get_cpu_vendor(struct cpuinfo_x86*); /* Disabled by DMI scan or kernel option? */ if (enable_local_apic < 0) return -1; /* Workaround for us being called before identify_cpu(). */ get_cpu_vendor(&boot_cpu_data); switch (boot_cpu_data.x86_vendor) { case X86_VENDOR_AMD: if (boot_cpu_data.x86 == 6 && boot_cpu_data.x86_model > 1) break; if (boot_cpu_data.x86 == 15 && cpu_has_apic) break; goto no_apic; case X86_VENDOR_INTEL: if (boot_cpu_data.x86 == 6 || (boot_cpu_data.x86 == 15 && (cpu_has_apic || enable_local_apic > 0)) || (boot_cpu_data.x86 == 5 && cpu_has_apic)) break; goto no_apic; default: goto no_apic; } if (!cpu_has_apic) { /* * Over-ride BIOS and try to enable LAPIC * only if "lapic" specified */ if (enable_local_apic != 1) goto no_apic; /* * Some BIOSes disable the local APIC in the * APIC_BASE MSR. This can only be done in * software for Intel P6 and AMD K7 (Model > 1). */ rdmsr(MSR_IA32_APICBASE, l, h); if (!(l & MSR_IA32_APICBASE_ENABLE)) { printk("Local APIC disabled by BIOS -- reenabling.\n"); l &= ~MSR_IA32_APICBASE_BASE; l |= MSR_IA32_APICBASE_ENABLE | APIC_DEFAULT_PHYS_BASE; wrmsr(MSR_IA32_APICBASE, l, h); enabled_via_apicbase = 1; } } /* * The APIC feature bit should now be enabled * in `cpuid' */ features = cpuid_edx(1); if (!(features & (1 << X86_FEATURE_APIC))) { printk("Could not enable APIC!\n"); return -1; } set_bit(X86_FEATURE_APIC, &boot_cpu_data.x86_capability); mp_lapic_addr = APIC_DEFAULT_PHYS_BASE; /* The BIOS may have set up the APIC at some other address */ rdmsr(MSR_IA32_APICBASE, l, h); if (l & MSR_IA32_APICBASE_ENABLE) mp_lapic_addr = l & MSR_IA32_APICBASE_BASE; if (nmi_watchdog != NMI_NONE) nmi_watchdog = NMI_LOCAL_APIC; printk("Found and enabled local APIC!\n"); apic_pm_init1(); return 0;no_apic: printk("No local APIC present or hardware disabled\n"); return -1;}void __init init_apic_mappings(void){ unsigned long apic_phys; /* * If no local APIC can be found then set up a fake all * zeroes page to simulate the local APIC and another * one for the IO-APIC. */ if (!smp_found_config && detect_init_APIC()) { apic_phys = (unsigned long) alloc_bootmem_pages(PAGE_SIZE); apic_phys = __pa(apic_phys); } else apic_phys = mp_lapic_addr; set_fixmap_nocache(FIX_APIC_BASE, apic_phys); Dprintk("mapped APIC to %08lx (%08lx)\n", APIC_BASE, apic_phys); /* * Fetch the APIC ID of the BSP in case we have a * default configuration (or the MP table is broken). */ if (boot_cpu_physical_apicid == -1U) boot_cpu_physical_apicid = GET_APIC_ID(apic_read(APIC_ID));#ifdef CONFIG_X86_IO_APIC { unsigned long ioapic_phys, idx = FIX_IO_APIC_BASE_0; int i; for (i = 0; i < nr_ioapics; i++) { if (smp_found_config) { ioapic_phys = mp_ioapics[i].mpc_apicaddr; if (!ioapic_phys) { printk(KERN_ERR "WARNING: bogus zero IO-APIC address found in MPTABLE, disabling IO/APIC support!\n"); smp_found_config = 0; skip_ioapic_setup = 1; goto fake_ioapic_page; } } else {fake_ioapic_page: ioapic_phys = (unsigned long) alloc_bootmem_pages(PAGE_SIZE); ioapic_phys = __pa(ioapic_phys); } set_fixmap_nocache(idx, ioapic_phys); Dprintk("mapped IOAPIC to %08lx (%08lx)\n", __fix_to_virt(idx), ioapic_phys); idx++; } }#endif}/* * This part sets up the APIC 32 bit clock in LVTT1, with HZ interrupts * per second. We assume that the caller has already set up the local * APIC. * * The APIC timer is not exactly sync with the external timer chip, it * closely follows bus clocks. *//* * 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, 0x43); count = inb_p(0x40); count |= inb_p(0x40) << 8; spin_unlock_irqrestore(&i8253_lock, flags); return count;}void __init wait_8254_wraparound(void){ unsigned int curr_count, prev_count=~0; int delta; curr_count = get_8254_timer_count(); do { prev_count = curr_count; curr_count = get_8254_timer_count(); delta = curr_count-prev_count; /* * This limit for delta seems arbitrary, but it isn't, it's * slightly above the level of error a buggy Mercury/Neptune * chipset timer can cause. */ } while (delta < 300);}/* * This function sets up the local APIC timer, with a timeout of * 'clocks' APIC bus clock. During calibration we actually call
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -