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