/** * @file op_fixmap.c * Horrible hacks for compatibility's sake. * Based in part on arch/i386/kernel/mpparse.c * * @remark Copyright 2002 OProfile authors * @remark Read the file COPYING * * @author John Levon * @author Philippe Elie */ #include <linux/mm.h> #include <linux/init.h> #include <linux/config.h> #include <linux/pagemap.h> #include <asm/io.h> #include "oprofile.h" #include "apic_compat.h" #ifndef cpu_has_pge #if V_BEFORE(2, 4, 0) #define cpu_has_pge (test_bit(X86_FEATURE_PGE, &boot_cpu_data.x86_capability)) #else #define cpu_has_pge (test_bit(X86_FEATURE_PGE, boot_cpu_data.x86_capability)) #endif #endif unsigned long virt_apic_base; /* some static commented out to avoid warning, trying to figure out * in exactly which circumstances we need this function is too prone * error to be made w/o a full rebuild of supported kernel version */ /* how about __attribute__(__unused__) then ? */ /* FIXME is this comment right ? */ /* We don't take care about locking mm->page_table_lock because this is * only needed on SMP and on SMP we have already a sensible setup */ /*static*/ void set_pte_phys(ulong vaddr, ulong phys) { pgprot_t prot; pgd_t * pgd; pmd_t * pmd; pte_t * pte; pgd = pgd_offset_k(vaddr); pmd = pmd_offset(pgd, vaddr); pte = pte_offset(pmd, vaddr); prot = PAGE_KERNEL; /* when !CONFIG_X86_LOCAL_APIC we can't rely on no cache flag set */ pgprot_val(prot) |= _PAGE_PCD; if (cpu_has_pge) pgprot_val(prot) |= _PAGE_GLOBAL; set_pte(pte, mk_pte_phys(phys, prot)); __flush_tlb_one(vaddr); } /*static*/ void alloc_fixmap(void) { /* dirty hack :/ */ virt_apic_base = (ulong)vmalloc(4096); set_pte_phys(virt_apic_base, APIC_DEFAULT_PHYS_BASE); } /*static*/ void free_fixmap(void) { ulong vaddr; pgd_t * pgd; pmd_t * pmd; pte_t * pte; vaddr = virt_apic_base; if (!vaddr) return; pgd = pgd_offset_k(vaddr); if (!pgd) return; pmd = pmd_offset(pgd, vaddr); if (!pmd) return; pte = pte_offset(pmd, vaddr); if (!pte) return; /* FIXME: is this the right way */ pte_clear(pte); __flush_tlb_one(vaddr); vfree((void*)virt_apic_base); } /* * Make sure we can access the APIC. Some kernel versions create * a meaningless zero-page mapping for the local APIC: we must * detect this case and reset it. * * Some kernel versions/configs won't map the APIC at all, in * which case we need to hack it ourselves. */ void fixmap_setup(void) { #if V_BEFORE(2, 4, 10) #if defined(CONFIG_X86_LOCAL_APIC) static int find_intel_smp(void); if (!find_intel_smp()) { set_pte_phys(__fix_to_virt(FIX_APIC_BASE), APIC_DEFAULT_PHYS_BASE); printk(KERN_INFO "oprofile: remapping local APIC.\n"); } #else alloc_fixmap(); printk(KERN_INFO "oprofile: mapping APIC.\n"); #endif /* CONFIG_X86_LOCAL_APIC */ #else #if !defined(CONFIG_X86_LOCAL_APIC) alloc_fixmap(); printk(KERN_INFO "oprofile: mapping APIC.\n"); #endif #endif } void fixmap_restore(void) { #if V_BEFORE(2, 4, 10) #if defined(CONFIG_X86_LOCAL_APIC) /* Nothing to do */ #else free_fixmap(); printk(KERN_INFO "oprofile: freeing APIC mapping.\n"); #endif /* CONFIG_X86_LOCAL_APIC */ #else #if !defined(CONFIG_X86_LOCAL_APIC) free_fixmap(); printk(KERN_INFO "oprofile: freeing APIC mapping.\n"); #endif #endif } /* ---------------- MP table code ------------------ */ #if V_BEFORE(2, 4, 10) && defined(CONFIG_X86_LOCAL_APIC) static int __init mpf_checksum(unsigned char * mp, int len) { int sum = 0; while (len--) sum += *mp++; return sum & 0xFF; } static int __init mpf_table_ok(struct intel_mp_floating * mpf, unsigned long * bp) { if (*bp != SMP_MAGIC_IDENT) return 0; if (mpf->mpf_length != 1) return 0; if (mpf_checksum((unsigned char *)bp, 16)) return 0; return (mpf->mpf_specification == 1 || mpf->mpf_specification == 4); } static int __init smp_scan_config (unsigned long base, unsigned long length) { unsigned long * bp = phys_to_virt(base); struct intel_mp_floating * mpf; while (length > 0) { mpf = (struct intel_mp_floating *)bp; if (mpf_table_ok(mpf, bp)) return 1; bp += 4; length -= 16; } return 0; } static int __init find_intel_smp(void) { unsigned int address; if (smp_scan_config(0x0, 0x400) || smp_scan_config(639*0x400, 0x400) || smp_scan_config(0xF0000, 0x10000)) return 1; address = *(unsigned short *)phys_to_virt(0x40E); address <<= 4; return smp_scan_config(address, 0x1000); } #endif /* V_BEFORE(2,4,10) && defined(CONFIG_X86_LOCAL_APIC) */