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
set_pte_phys(ulong vaddr,ulong phys)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
alloc_fixmap(void)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
free_fixmap(void)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 */
fixmap_setup(void)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
fixmap_restore(void)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
mpf_checksum(unsigned char * mp,int len)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
mpf_table_ok(struct intel_mp_floating * mpf,unsigned long * bp)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
smp_scan_config(unsigned long base,unsigned long length)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
find_intel_smp(void)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