diff options
author | David S. Miller <davem@davemloft.net> | 2011-07-29 02:31:26 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2011-08-15 21:31:40 -0400 |
commit | f1b7f4b6d299fcad9b6bb84d86cef2094613ea75 (patch) | |
tree | a8564e9cfa6020bbcfaa6105f9b6f0e574c896cf | |
parent | b25f76a8fe3230786712d18a83c7a57b7abc6624 (diff) |
sparc: Sanitize cpu feature detection and reporting.
[ Upstream commit ac85fe8b21248054851e05bfaa352562e5b06dd3 ]
Instead of evaluating the cpu features for ELF_HWCAP every exec,
calculate it once at boot time.
Add AV_SPARC_* capability flag bits, compatible with what Solaris
reports to applications.
Report these capabilities once in the kernel log, and also via
/proc/cpuinfo in a new "cpucaps" entry.
If available, fetch the cpu features from the machine description
'hwcap-list' property of the 'cpu' node.
Signed-off-by: David S. Miller <davem@davemloft.net>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
-rw-r--r-- | arch/sparc/include/asm/elf_64.h | 65 | ||||
-rw-r--r-- | arch/sparc/kernel/cpu.c | 1 | ||||
-rw-r--r-- | arch/sparc/kernel/kernel.h | 6 | ||||
-rw-r--r-- | arch/sparc/kernel/setup_64.c | 149 |
4 files changed, 185 insertions, 36 deletions
diff --git a/arch/sparc/include/asm/elf_64.h b/arch/sparc/include/asm/elf_64.h index af1539839b7..7df8b7f544d 100644 --- a/arch/sparc/include/asm/elf_64.h +++ b/arch/sparc/include/asm/elf_64.h | |||
@@ -59,15 +59,33 @@ | |||
59 | #define R_SPARC_6 45 | 59 | #define R_SPARC_6 45 |
60 | 60 | ||
61 | /* Bits present in AT_HWCAP, primarily for Sparc32. */ | 61 | /* Bits present in AT_HWCAP, primarily for Sparc32. */ |
62 | 62 | #define HWCAP_SPARC_FLUSH 0x00000001 | |
63 | #define HWCAP_SPARC_FLUSH 1 /* CPU supports flush instruction. */ | 63 | #define HWCAP_SPARC_STBAR 0x00000002 |
64 | #define HWCAP_SPARC_STBAR 2 | 64 | #define HWCAP_SPARC_SWAP 0x00000004 |
65 | #define HWCAP_SPARC_SWAP 4 | 65 | #define HWCAP_SPARC_MULDIV 0x00000008 |
66 | #define HWCAP_SPARC_MULDIV 8 | 66 | #define HWCAP_SPARC_V9 0x00000010 |
67 | #define HWCAP_SPARC_V9 16 | 67 | #define HWCAP_SPARC_ULTRA3 0x00000020 |
68 | #define HWCAP_SPARC_ULTRA3 32 | 68 | #define HWCAP_SPARC_BLKINIT 0x00000040 |
69 | #define HWCAP_SPARC_BLKINIT 64 | 69 | #define HWCAP_SPARC_N2 0x00000080 |
70 | #define HWCAP_SPARC_N2 128 | 70 | |
71 | /* Solaris compatible AT_HWCAP bits. */ | ||
72 | #define AV_SPARC_MUL32 0x00000100 /* 32x32 multiply is efficient */ | ||
73 | #define AV_SPARC_DIV32 0x00000200 /* 32x32 divide is efficient */ | ||
74 | #define AV_SPARC_FSMULD 0x00000400 /* 'fsmuld' is efficient */ | ||
75 | #define AV_SPARC_V8PLUS 0x00000800 /* v9 insn available to 32bit */ | ||
76 | #define AV_SPARC_POPC 0x00001000 /* 'popc' is efficient */ | ||
77 | #define AV_SPARC_VIS 0x00002000 /* VIS insns available */ | ||
78 | #define AV_SPARC_VIS2 0x00004000 /* VIS2 insns available */ | ||
79 | #define AV_SPARC_ASI_BLK_INIT 0x00008000 /* block init ASIs available */ | ||
80 | #define AV_SPARC_FMAF 0x00010000 /* fused multiply-add */ | ||
81 | #define AV_SPARC_VIS3 0x00020000 /* VIS3 insns available */ | ||
82 | #define AV_SPARC_HPC 0x00040000 /* HPC insns available */ | ||
83 | #define AV_SPARC_RANDOM 0x00080000 /* 'random' insn available */ | ||
84 | #define AV_SPARC_TRANS 0x00100000 /* transaction insns available */ | ||
85 | #define AV_SPARC_FJFMAU 0x00200000 /* unfused multiply-add */ | ||
86 | #define AV_SPARC_IMA 0x00400000 /* integer multiply-add */ | ||
87 | #define AV_SPARC_ASI_CACHE_SPARING \ | ||
88 | 0x00800000 /* cache sparing ASIs available */ | ||
71 | 89 | ||
72 | #define CORE_DUMP_USE_REGSET | 90 | #define CORE_DUMP_USE_REGSET |
73 | 91 | ||
@@ -162,33 +180,8 @@ typedef struct { | |||
162 | #define ELF_ET_DYN_BASE 0x0000010000000000UL | 180 | #define ELF_ET_DYN_BASE 0x0000010000000000UL |
163 | #define COMPAT_ELF_ET_DYN_BASE 0x0000000070000000UL | 181 | #define COMPAT_ELF_ET_DYN_BASE 0x0000000070000000UL |
164 | 182 | ||
165 | 183 | extern unsigned long sparc64_elf_hwcap; | |
166 | /* This yields a mask that user programs can use to figure out what | 184 | #define ELF_HWCAP sparc64_elf_hwcap |
167 | instruction set this cpu supports. */ | ||
168 | |||
169 | /* On Ultra, we support all of the v8 capabilities. */ | ||
170 | static inline unsigned int sparc64_elf_hwcap(void) | ||
171 | { | ||
172 | unsigned int cap = (HWCAP_SPARC_FLUSH | HWCAP_SPARC_STBAR | | ||
173 | HWCAP_SPARC_SWAP | HWCAP_SPARC_MULDIV | | ||
174 | HWCAP_SPARC_V9); | ||
175 | |||
176 | if (tlb_type == cheetah || tlb_type == cheetah_plus) | ||
177 | cap |= HWCAP_SPARC_ULTRA3; | ||
178 | else if (tlb_type == hypervisor) { | ||
179 | if (sun4v_chip_type == SUN4V_CHIP_NIAGARA1 || | ||
180 | sun4v_chip_type == SUN4V_CHIP_NIAGARA2 || | ||
181 | sun4v_chip_type == SUN4V_CHIP_NIAGARA3) | ||
182 | cap |= HWCAP_SPARC_BLKINIT; | ||
183 | if (sun4v_chip_type == SUN4V_CHIP_NIAGARA2 || | ||
184 | sun4v_chip_type == SUN4V_CHIP_NIAGARA3) | ||
185 | cap |= HWCAP_SPARC_N2; | ||
186 | } | ||
187 | |||
188 | return cap; | ||
189 | } | ||
190 | |||
191 | #define ELF_HWCAP sparc64_elf_hwcap(); | ||
192 | 185 | ||
193 | /* This yields a string that ld.so will use to load implementation | 186 | /* This yields a string that ld.so will use to load implementation |
194 | specific libraries for optimization. This is more specific in | 187 | specific libraries for optimization. This is more specific in |
diff --git a/arch/sparc/kernel/cpu.c b/arch/sparc/kernel/cpu.c index 17cf290dc2b..9810fd88105 100644 --- a/arch/sparc/kernel/cpu.c +++ b/arch/sparc/kernel/cpu.c | |||
@@ -396,6 +396,7 @@ static int show_cpuinfo(struct seq_file *m, void *__unused) | |||
396 | , cpu_data(0).clock_tick | 396 | , cpu_data(0).clock_tick |
397 | #endif | 397 | #endif |
398 | ); | 398 | ); |
399 | cpucap_info(m); | ||
399 | #ifdef CONFIG_SMP | 400 | #ifdef CONFIG_SMP |
400 | smp_bogo(m); | 401 | smp_bogo(m); |
401 | #endif | 402 | #endif |
diff --git a/arch/sparc/kernel/kernel.h b/arch/sparc/kernel/kernel.h index 6f6544cfa0e..8325d775938 100644 --- a/arch/sparc/kernel/kernel.h +++ b/arch/sparc/kernel/kernel.h | |||
@@ -10,6 +10,12 @@ extern const char *sparc_pmu_type; | |||
10 | extern unsigned int fsr_storage; | 10 | extern unsigned int fsr_storage; |
11 | extern int ncpus_probed; | 11 | extern int ncpus_probed; |
12 | 12 | ||
13 | #ifdef CONFIG_SPARC64 | ||
14 | /* setup_64.c */ | ||
15 | struct seq_file; | ||
16 | extern void cpucap_info(struct seq_file *); | ||
17 | #endif | ||
18 | |||
13 | #ifdef CONFIG_SPARC32 | 19 | #ifdef CONFIG_SPARC32 |
14 | /* cpu.c */ | 20 | /* cpu.c */ |
15 | extern void cpu_probe(void); | 21 | extern void cpu_probe(void); |
diff --git a/arch/sparc/kernel/setup_64.c b/arch/sparc/kernel/setup_64.c index c4dd0999da8..242dbb3f31d 100644 --- a/arch/sparc/kernel/setup_64.c +++ b/arch/sparc/kernel/setup_64.c | |||
@@ -29,6 +29,7 @@ | |||
29 | #include <linux/interrupt.h> | 29 | #include <linux/interrupt.h> |
30 | #include <linux/cpu.h> | 30 | #include <linux/cpu.h> |
31 | #include <linux/initrd.h> | 31 | #include <linux/initrd.h> |
32 | #include <linux/module.h> | ||
32 | 33 | ||
33 | #include <asm/system.h> | 34 | #include <asm/system.h> |
34 | #include <asm/io.h> | 35 | #include <asm/io.h> |
@@ -46,6 +47,8 @@ | |||
46 | #include <asm/mmu.h> | 47 | #include <asm/mmu.h> |
47 | #include <asm/ns87303.h> | 48 | #include <asm/ns87303.h> |
48 | #include <asm/btext.h> | 49 | #include <asm/btext.h> |
50 | #include <asm/elf.h> | ||
51 | #include <asm/mdesc.h> | ||
49 | 52 | ||
50 | #ifdef CONFIG_IP_PNP | 53 | #ifdef CONFIG_IP_PNP |
51 | #include <net/ipconfig.h> | 54 | #include <net/ipconfig.h> |
@@ -278,6 +281,151 @@ void __init boot_cpu_id_too_large(int cpu) | |||
278 | } | 281 | } |
279 | #endif | 282 | #endif |
280 | 283 | ||
284 | /* On Ultra, we support all of the v8 capabilities. */ | ||
285 | unsigned long sparc64_elf_hwcap = (HWCAP_SPARC_FLUSH | HWCAP_SPARC_STBAR | | ||
286 | HWCAP_SPARC_SWAP | HWCAP_SPARC_MULDIV | | ||
287 | HWCAP_SPARC_V9); | ||
288 | EXPORT_SYMBOL(sparc64_elf_hwcap); | ||
289 | |||
290 | static const char *hwcaps[] = { | ||
291 | "flush", "stbar", "swap", "muldiv", "v9", | ||
292 | "ultra3", "blkinit", "n2", | ||
293 | |||
294 | /* These strings are as they appear in the machine description | ||
295 | * 'hwcap-list' property for cpu nodes. | ||
296 | */ | ||
297 | "mul32", "div32", "fsmuld", "v8plus", "popc", "vis", "vis2", | ||
298 | "ASIBlkInit", "fmaf", "vis3", "hpc", "random", "trans", "fjfmau", | ||
299 | "ima", "cspare", | ||
300 | }; | ||
301 | |||
302 | void cpucap_info(struct seq_file *m) | ||
303 | { | ||
304 | unsigned long caps = sparc64_elf_hwcap; | ||
305 | int i, printed = 0; | ||
306 | |||
307 | seq_puts(m, "cpucaps\t\t: "); | ||
308 | for (i = 0; i < ARRAY_SIZE(hwcaps); i++) { | ||
309 | unsigned long bit = 1UL << i; | ||
310 | if (caps & bit) { | ||
311 | seq_printf(m, "%s%s", | ||
312 | printed ? "," : "", hwcaps[i]); | ||
313 | printed++; | ||
314 | } | ||
315 | } | ||
316 | seq_putc(m, '\n'); | ||
317 | } | ||
318 | |||
319 | static void __init report_hwcaps(unsigned long caps) | ||
320 | { | ||
321 | int i, printed = 0; | ||
322 | |||
323 | printk(KERN_INFO "CPU CAPS: ["); | ||
324 | for (i = 0; i < ARRAY_SIZE(hwcaps); i++) { | ||
325 | unsigned long bit = 1UL << i; | ||
326 | if (caps & bit) { | ||
327 | printk(KERN_CONT "%s%s", | ||
328 | printed ? "," : "", hwcaps[i]); | ||
329 | if (++printed == 8) { | ||
330 | printk(KERN_CONT "]\n"); | ||
331 | printk(KERN_INFO "CPU CAPS: ["); | ||
332 | printed = 0; | ||
333 | } | ||
334 | } | ||
335 | } | ||
336 | printk(KERN_CONT "]\n"); | ||
337 | } | ||
338 | |||
339 | static unsigned long __init mdesc_cpu_hwcap_list(void) | ||
340 | { | ||
341 | struct mdesc_handle *hp; | ||
342 | unsigned long caps = 0; | ||
343 | const char *prop; | ||
344 | int len; | ||
345 | u64 pn; | ||
346 | |||
347 | hp = mdesc_grab(); | ||
348 | if (!hp) | ||
349 | return 0; | ||
350 | |||
351 | pn = mdesc_node_by_name(hp, MDESC_NODE_NULL, "cpu"); | ||
352 | if (pn == MDESC_NODE_NULL) | ||
353 | goto out; | ||
354 | |||
355 | prop = mdesc_get_property(hp, pn, "hwcap-list", &len); | ||
356 | if (!prop) | ||
357 | goto out; | ||
358 | |||
359 | while (len) { | ||
360 | int i, plen; | ||
361 | |||
362 | for (i = 0; i < ARRAY_SIZE(hwcaps); i++) { | ||
363 | unsigned long bit = 1UL << i; | ||
364 | |||
365 | if (!strcmp(prop, hwcaps[i])) { | ||
366 | caps |= bit; | ||
367 | break; | ||
368 | } | ||
369 | } | ||
370 | |||
371 | plen = strlen(prop) + 1; | ||
372 | prop += plen; | ||
373 | len -= plen; | ||
374 | } | ||
375 | |||
376 | out: | ||
377 | mdesc_release(hp); | ||
378 | return caps; | ||
379 | } | ||
380 | |||
381 | /* This yields a mask that user programs can use to figure out what | ||
382 | * instruction set this cpu supports. | ||
383 | */ | ||
384 | static void __init init_sparc64_elf_hwcap(void) | ||
385 | { | ||
386 | unsigned long cap = sparc64_elf_hwcap; | ||
387 | unsigned long mdesc_caps; | ||
388 | |||
389 | if (tlb_type == cheetah || tlb_type == cheetah_plus) | ||
390 | cap |= HWCAP_SPARC_ULTRA3; | ||
391 | else if (tlb_type == hypervisor) { | ||
392 | if (sun4v_chip_type == SUN4V_CHIP_NIAGARA1 || | ||
393 | sun4v_chip_type == SUN4V_CHIP_NIAGARA2 || | ||
394 | sun4v_chip_type == SUN4V_CHIP_NIAGARA3) | ||
395 | cap |= HWCAP_SPARC_BLKINIT; | ||
396 | if (sun4v_chip_type == SUN4V_CHIP_NIAGARA2 || | ||
397 | sun4v_chip_type == SUN4V_CHIP_NIAGARA3) | ||
398 | cap |= HWCAP_SPARC_N2; | ||
399 | } | ||
400 | |||
401 | cap |= (AV_SPARC_MUL32 | AV_SPARC_DIV32 | AV_SPARC_V8PLUS); | ||
402 | |||
403 | mdesc_caps = mdesc_cpu_hwcap_list(); | ||
404 | if (!mdesc_caps) { | ||
405 | if (tlb_type == spitfire) | ||
406 | cap |= AV_SPARC_VIS; | ||
407 | if (tlb_type == cheetah || tlb_type == cheetah_plus) | ||
408 | cap |= AV_SPARC_VIS | AV_SPARC_VIS2; | ||
409 | if (tlb_type == cheetah_plus) | ||
410 | cap |= AV_SPARC_POPC; | ||
411 | if (tlb_type == hypervisor) { | ||
412 | if (sun4v_chip_type == SUN4V_CHIP_NIAGARA1) | ||
413 | cap |= AV_SPARC_ASI_BLK_INIT; | ||
414 | if (sun4v_chip_type == SUN4V_CHIP_NIAGARA2 || | ||
415 | sun4v_chip_type == SUN4V_CHIP_NIAGARA3) | ||
416 | cap |= (AV_SPARC_VIS | AV_SPARC_VIS2 | | ||
417 | AV_SPARC_ASI_BLK_INIT | | ||
418 | AV_SPARC_POPC); | ||
419 | if (sun4v_chip_type == SUN4V_CHIP_NIAGARA3) | ||
420 | cap |= (AV_SPARC_VIS3 | AV_SPARC_HPC | | ||
421 | AV_SPARC_FMAF); | ||
422 | } | ||
423 | } | ||
424 | sparc64_elf_hwcap = cap | mdesc_caps; | ||
425 | |||
426 | report_hwcaps(sparc64_elf_hwcap); | ||
427 | } | ||
428 | |||
281 | void __init setup_arch(char **cmdline_p) | 429 | void __init setup_arch(char **cmdline_p) |
282 | { | 430 | { |
283 | /* Initialize PROM console and command line. */ | 431 | /* Initialize PROM console and command line. */ |
@@ -337,6 +485,7 @@ void __init setup_arch(char **cmdline_p) | |||
337 | init_cur_cpu_trap(current_thread_info()); | 485 | init_cur_cpu_trap(current_thread_info()); |
338 | 486 | ||
339 | paging_init(); | 487 | paging_init(); |
488 | init_sparc64_elf_hwcap(); | ||
340 | } | 489 | } |
341 | 490 | ||
342 | extern int stop_a_enabled; | 491 | extern int stop_a_enabled; |