Home | History | Annotate | Download | only in x86
      1 /**
      2  * @file op_fixmap.c
      3  * Horrible hacks for compatibility's sake.
      4  * Based in part on arch/i386/kernel/mpparse.c
      5  *
      6  * @remark Copyright 2002 OProfile authors
      7  * @remark Read the file COPYING
      8  *
      9  * @author John Levon
     10  * @author Philippe Elie
     11  */
     12 
     13 #include <linux/mm.h>
     14 #include <linux/init.h>
     15 #include <linux/config.h>
     16 #include <linux/pagemap.h>
     17 #include <asm/io.h>
     18 
     19 #include "oprofile.h"
     20 #include "apic_compat.h"
     21 
     22 #ifndef cpu_has_pge
     23 #if V_BEFORE(2, 4, 0)
     24 #define cpu_has_pge (test_bit(X86_FEATURE_PGE, &boot_cpu_data.x86_capability))
     25 #else
     26 #define cpu_has_pge (test_bit(X86_FEATURE_PGE, boot_cpu_data.x86_capability))
     27 #endif
     28 #endif
     29 
     30 unsigned long virt_apic_base;
     31 
     32 /* some static commented out to avoid warning, trying to figure out
     33  * in exactly which circumstances we need this function is too prone
     34  * error to be made w/o a full rebuild of supported kernel version */
     35 /* how about __attribute__(__unused__) then ? */
     36 
     37 /* FIXME is this comment right ? */
     38 /* We don't take care about locking mm->page_table_lock because this is
     39  * only needed on SMP and on SMP we have already a sensible setup */
     40 
     41 /*static*/ void set_pte_phys(ulong vaddr, ulong phys)
     42 {
     43 	pgprot_t prot;
     44 	pgd_t * pgd;
     45 	pmd_t * pmd;
     46 	pte_t * pte;
     47 
     48 	pgd = pgd_offset_k(vaddr);
     49 	pmd = pmd_offset(pgd, vaddr);
     50 	pte = pte_offset(pmd, vaddr);
     51 	prot = PAGE_KERNEL;
     52 	/* when !CONFIG_X86_LOCAL_APIC we can't rely on no cache flag set */
     53 	pgprot_val(prot) |= _PAGE_PCD;
     54 	if (cpu_has_pge)
     55 		pgprot_val(prot) |= _PAGE_GLOBAL;
     56 	set_pte(pte, mk_pte_phys(phys, prot));
     57 	__flush_tlb_one(vaddr);
     58 }
     59 
     60 /*static*/ void alloc_fixmap(void)
     61 {
     62 	/* dirty hack :/ */
     63 	virt_apic_base = (ulong)vmalloc(4096);
     64 	set_pte_phys(virt_apic_base, APIC_DEFAULT_PHYS_BASE);
     65 }
     66 
     67 /*static*/ void free_fixmap(void)
     68 {
     69 	ulong vaddr;
     70 	pgd_t * pgd;
     71 	pmd_t * pmd;
     72 	pte_t * pte;
     73 
     74 	vaddr = virt_apic_base;
     75 	if (!vaddr)
     76 		return;
     77 
     78 	pgd = pgd_offset_k(vaddr);
     79 	if (!pgd)
     80 		return;
     81 
     82 	pmd = pmd_offset(pgd, vaddr);
     83 	if (!pmd)
     84 		return;
     85 
     86 	pte = pte_offset(pmd, vaddr);
     87 	if (!pte)
     88 		return;
     89 
     90 	/* FIXME: is this the right way */
     91 	pte_clear(pte);
     92 	__flush_tlb_one(vaddr);
     93 
     94 	vfree((void*)virt_apic_base);
     95 }
     96 
     97 /*
     98  * Make sure we can access the APIC. Some kernel versions create
     99  * a meaningless zero-page mapping for the local APIC: we must
    100  * detect this case and reset it.
    101  *
    102  * Some kernel versions/configs won't map the APIC at all, in
    103  * which case we need to hack it ourselves.
    104  */
    105 void fixmap_setup(void)
    106 {
    107 #if V_BEFORE(2, 4, 10)
    108 #if defined(CONFIG_X86_LOCAL_APIC)
    109 	static int find_intel_smp(void);
    110 
    111 	if (!find_intel_smp()) {
    112 		set_pte_phys(__fix_to_virt(FIX_APIC_BASE),
    113 			APIC_DEFAULT_PHYS_BASE);
    114 		printk(KERN_INFO "oprofile: remapping local APIC.\n");
    115 	}
    116 #else
    117 	alloc_fixmap();
    118 	printk(KERN_INFO "oprofile: mapping APIC.\n");
    119 #endif /* CONFIG_X86_LOCAL_APIC */
    120 #else
    121 #if !defined(CONFIG_X86_LOCAL_APIC)
    122 	alloc_fixmap();
    123 	printk(KERN_INFO "oprofile: mapping APIC.\n");
    124 #endif
    125 #endif
    126 }
    127 
    128 void fixmap_restore(void)
    129 {
    130 #if V_BEFORE(2, 4, 10)
    131 #if defined(CONFIG_X86_LOCAL_APIC)
    132 	/* Nothing to do */
    133 #else
    134 	free_fixmap();
    135 	printk(KERN_INFO "oprofile: freeing APIC mapping.\n");
    136 #endif /* CONFIG_X86_LOCAL_APIC */
    137 #else
    138 #if !defined(CONFIG_X86_LOCAL_APIC)
    139 	free_fixmap();
    140 	printk(KERN_INFO "oprofile: freeing APIC mapping.\n");
    141 #endif
    142 #endif
    143 }
    144 
    145 /* ---------------- MP table code ------------------ */
    146 
    147 #if V_BEFORE(2, 4, 10) && defined(CONFIG_X86_LOCAL_APIC)
    148 
    149 static int __init mpf_checksum(unsigned char * mp, int len)
    150 {
    151 	int sum = 0;
    152 
    153 	while (len--)
    154 		sum += *mp++;
    155 
    156 	return sum & 0xFF;
    157 }
    158 
    159 static int __init mpf_table_ok(struct intel_mp_floating * mpf, unsigned long * bp)
    160 {
    161 	if (*bp != SMP_MAGIC_IDENT)
    162 		return 0;
    163 	if (mpf->mpf_length != 1)
    164 		return 0;
    165 	if (mpf_checksum((unsigned char *)bp, 16))
    166 		return 0;
    167 
    168 	return (mpf->mpf_specification == 1 || mpf->mpf_specification == 4);
    169 }
    170 
    171 static int __init smp_scan_config (unsigned long base, unsigned long length)
    172 {
    173 	unsigned long * bp = phys_to_virt(base);
    174 	struct intel_mp_floating * mpf;
    175 
    176 	while (length > 0) {
    177 		mpf = (struct intel_mp_floating *)bp;
    178 		if (mpf_table_ok(mpf, bp))
    179 			return 1;
    180 		bp += 4;
    181 		length -= 16;
    182 	}
    183 	return 0;
    184 }
    185 
    186 static int __init find_intel_smp(void)
    187 {
    188 	unsigned int address;
    189 
    190 	if (smp_scan_config(0x0, 0x400) ||
    191 		smp_scan_config(639*0x400, 0x400) ||
    192 		smp_scan_config(0xF0000, 0x10000))
    193 		return 1;
    194 
    195 	address = *(unsigned short *)phys_to_virt(0x40E);
    196 	address <<= 4;
    197 	return smp_scan_config(address, 0x1000);
    198 }
    199 
    200 #endif /* V_BEFORE(2,4,10) && defined(CONFIG_X86_LOCAL_APIC) */
    201