• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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