aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKees Cook <keescook@chromium.org>2013-10-10 20:18:13 -0400
committerH. Peter Anvin <hpa@linux.intel.com>2013-10-13 06:12:02 -0400
commitdd78b97367bd575918204cc89107c1479d3fc1a7 (patch)
tree145b40fe13f64045d0a5e1063ac17357e9fad0de
parentd751c169e9a6f0f853346f1184881422bd10b3c2 (diff)
x86, boot: Move CPU flags out of cpucheck
Refactor the CPU flags handling out of the cpucheck routines so that they can be reused by the future ASLR routines (in order to detect CPU features like RDRAND and RDTSC). This reworks has_eflag() and has_fpu() to be used on both 32-bit and 64-bit, and refactors the calls to cpuid to make them PIC-safe on 32-bit. Signed-off-by: Kees Cook <keescook@chromium.org> Link: http://lkml.kernel.org/r/1381450698-28710-2-git-send-email-keescook@chromium.org Signed-off-by: H. Peter Anvin <hpa@linux.intel.com>
-rw-r--r--arch/x86/boot/Makefile2
-rw-r--r--arch/x86/boot/boot.h10
-rw-r--r--arch/x86/boot/compressed/Makefile2
-rw-r--r--arch/x86/boot/compressed/cpuflags.c12
-rw-r--r--arch/x86/boot/cpucheck.c86
-rw-r--r--arch/x86/boot/cpuflags.c104
-rw-r--r--arch/x86/boot/cpuflags.h19
7 files changed, 138 insertions, 97 deletions
diff --git a/arch/x86/boot/Makefile b/arch/x86/boot/Makefile
index 379814bc41e3..0da2e37b37c3 100644
--- a/arch/x86/boot/Makefile
+++ b/arch/x86/boot/Makefile
@@ -20,7 +20,7 @@ targets := vmlinux.bin setup.bin setup.elf bzImage
20targets += fdimage fdimage144 fdimage288 image.iso mtools.conf 20targets += fdimage fdimage144 fdimage288 image.iso mtools.conf
21subdir- := compressed 21subdir- := compressed
22 22
23setup-y += a20.o bioscall.o cmdline.o copy.o cpu.o cpucheck.o 23setup-y += a20.o bioscall.o cmdline.o copy.o cpu.o cpuflags.o cpucheck.o
24setup-y += early_serial_console.o edd.o header.o main.o mca.o memory.o 24setup-y += early_serial_console.o edd.o header.o main.o mca.o memory.o
25setup-y += pm.o pmjump.o printf.o regs.o string.o tty.o video.o 25setup-y += pm.o pmjump.o printf.o regs.o string.o tty.o video.o
26setup-y += video-mode.o version.o 26setup-y += video-mode.o version.o
diff --git a/arch/x86/boot/boot.h b/arch/x86/boot/boot.h
index ef72baeff484..50f8c5e0f37e 100644
--- a/arch/x86/boot/boot.h
+++ b/arch/x86/boot/boot.h
@@ -26,9 +26,8 @@
26#include <asm/boot.h> 26#include <asm/boot.h>
27#include <asm/setup.h> 27#include <asm/setup.h>
28#include "bitops.h" 28#include "bitops.h"
29#include <asm/cpufeature.h>
30#include <asm/processor-flags.h>
31#include "ctype.h" 29#include "ctype.h"
30#include "cpuflags.h"
32 31
33/* Useful macros */ 32/* Useful macros */
34#define BUILD_BUG_ON(condition) ((void)sizeof(char[1 - 2*!!(condition)])) 33#define BUILD_BUG_ON(condition) ((void)sizeof(char[1 - 2*!!(condition)]))
@@ -307,14 +306,7 @@ static inline int cmdline_find_option_bool(const char *option)
307 return __cmdline_find_option_bool(cmd_line_ptr, option); 306 return __cmdline_find_option_bool(cmd_line_ptr, option);
308} 307}
309 308
310
311/* cpu.c, cpucheck.c */ 309/* cpu.c, cpucheck.c */
312struct cpu_features {
313 int level; /* Family, or 64 for x86-64 */
314 int model;
315 u32 flags[NCAPINTS];
316};
317extern struct cpu_features cpu;
318int check_cpu(int *cpu_level_ptr, int *req_level_ptr, u32 **err_flags_ptr); 310int check_cpu(int *cpu_level_ptr, int *req_level_ptr, u32 **err_flags_ptr);
319int validate_cpu(void); 311int validate_cpu(void);
320 312
diff --git a/arch/x86/boot/compressed/Makefile b/arch/x86/boot/compressed/Makefile
index dcd90df10ab4..3312f1be9fd9 100644
--- a/arch/x86/boot/compressed/Makefile
+++ b/arch/x86/boot/compressed/Makefile
@@ -27,7 +27,7 @@ HOST_EXTRACFLAGS += -I$(srctree)/tools/include
27 27
28VMLINUX_OBJS = $(obj)/vmlinux.lds $(obj)/head_$(BITS).o $(obj)/misc.o \ 28VMLINUX_OBJS = $(obj)/vmlinux.lds $(obj)/head_$(BITS).o $(obj)/misc.o \
29 $(obj)/string.o $(obj)/cmdline.o $(obj)/early_serial_console.o \ 29 $(obj)/string.o $(obj)/cmdline.o $(obj)/early_serial_console.o \
30 $(obj)/piggy.o 30 $(obj)/piggy.o $(obj)/cpuflags.o
31 31
32$(obj)/eboot.o: KBUILD_CFLAGS += -fshort-wchar -mno-red-zone 32$(obj)/eboot.o: KBUILD_CFLAGS += -fshort-wchar -mno-red-zone
33 33
diff --git a/arch/x86/boot/compressed/cpuflags.c b/arch/x86/boot/compressed/cpuflags.c
new file mode 100644
index 000000000000..931cba6a4bb0
--- /dev/null
+++ b/arch/x86/boot/compressed/cpuflags.c
@@ -0,0 +1,12 @@
1#ifdef CONFIG_RANDOMIZE_BASE
2
3#include "../cpuflags.c"
4
5bool has_cpuflag(int flag)
6{
7 get_flags();
8
9 return test_bit(flag, cpu.flags);
10}
11
12#endif
diff --git a/arch/x86/boot/cpucheck.c b/arch/x86/boot/cpucheck.c
index 4d3ff037201f..e1f3c166a512 100644
--- a/arch/x86/boot/cpucheck.c
+++ b/arch/x86/boot/cpucheck.c
@@ -28,8 +28,6 @@
28#include <asm/required-features.h> 28#include <asm/required-features.h>
29#include <asm/msr-index.h> 29#include <asm/msr-index.h>
30 30
31struct cpu_features cpu;
32static u32 cpu_vendor[3];
33static u32 err_flags[NCAPINTS]; 31static u32 err_flags[NCAPINTS];
34 32
35static const int req_level = CONFIG_X86_MINIMUM_CPU_FAMILY; 33static const int req_level = CONFIG_X86_MINIMUM_CPU_FAMILY;
@@ -69,90 +67,6 @@ static int is_transmeta(void)
69 cpu_vendor[2] == A32('M', 'x', '8', '6'); 67 cpu_vendor[2] == A32('M', 'x', '8', '6');
70} 68}
71 69
72static int has_fpu(void)
73{
74 u16 fcw = -1, fsw = -1;
75 u32 cr0;
76
77 asm("movl %%cr0,%0" : "=r" (cr0));
78 if (cr0 & (X86_CR0_EM|X86_CR0_TS)) {
79 cr0 &= ~(X86_CR0_EM|X86_CR0_TS);
80 asm volatile("movl %0,%%cr0" : : "r" (cr0));
81 }
82
83 asm volatile("fninit ; fnstsw %0 ; fnstcw %1"
84 : "+m" (fsw), "+m" (fcw));
85
86 return fsw == 0 && (fcw & 0x103f) == 0x003f;
87}
88
89static int has_eflag(u32 mask)
90{
91 u32 f0, f1;
92
93 asm("pushfl ; "
94 "pushfl ; "
95 "popl %0 ; "
96 "movl %0,%1 ; "
97 "xorl %2,%1 ; "
98 "pushl %1 ; "
99 "popfl ; "
100 "pushfl ; "
101 "popl %1 ; "
102 "popfl"
103 : "=&r" (f0), "=&r" (f1)
104 : "ri" (mask));
105
106 return !!((f0^f1) & mask);
107}
108
109static void get_flags(void)
110{
111 u32 max_intel_level, max_amd_level;
112 u32 tfms;
113
114 if (has_fpu())
115 set_bit(X86_FEATURE_FPU, cpu.flags);
116
117 if (has_eflag(X86_EFLAGS_ID)) {
118 asm("cpuid"
119 : "=a" (max_intel_level),
120 "=b" (cpu_vendor[0]),
121 "=d" (cpu_vendor[1]),
122 "=c" (cpu_vendor[2])
123 : "a" (0));
124
125 if (max_intel_level >= 0x00000001 &&
126 max_intel_level <= 0x0000ffff) {
127 asm("cpuid"
128 : "=a" (tfms),
129 "=c" (cpu.flags[4]),
130 "=d" (cpu.flags[0])
131 : "a" (0x00000001)
132 : "ebx");
133 cpu.level = (tfms >> 8) & 15;
134 cpu.model = (tfms >> 4) & 15;
135 if (cpu.level >= 6)
136 cpu.model += ((tfms >> 16) & 0xf) << 4;
137 }
138
139 asm("cpuid"
140 : "=a" (max_amd_level)
141 : "a" (0x80000000)
142 : "ebx", "ecx", "edx");
143
144 if (max_amd_level >= 0x80000001 &&
145 max_amd_level <= 0x8000ffff) {
146 u32 eax = 0x80000001;
147 asm("cpuid"
148 : "+a" (eax),
149 "=c" (cpu.flags[6]),
150 "=d" (cpu.flags[1])
151 : : "ebx");
152 }
153 }
154}
155
156/* Returns a bitmask of which words we have error bits in */ 70/* Returns a bitmask of which words we have error bits in */
157static int check_flags(void) 71static int check_flags(void)
158{ 72{
diff --git a/arch/x86/boot/cpuflags.c b/arch/x86/boot/cpuflags.c
new file mode 100644
index 000000000000..b02544a2bce0
--- /dev/null
+++ b/arch/x86/boot/cpuflags.c
@@ -0,0 +1,104 @@
1#include <linux/types.h>
2#include "bitops.h"
3
4#include <asm/processor-flags.h>
5#include <asm/required-features.h>
6#include <asm/msr-index.h>
7#include "cpuflags.h"
8
9struct cpu_features cpu;
10u32 cpu_vendor[3];
11
12static bool loaded_flags;
13
14static int has_fpu(void)
15{
16 u16 fcw = -1, fsw = -1;
17 unsigned long cr0;
18
19 asm volatile("mov %%cr0,%0" : "=r" (cr0));
20 if (cr0 & (X86_CR0_EM|X86_CR0_TS)) {
21 cr0 &= ~(X86_CR0_EM|X86_CR0_TS);
22 asm volatile("mov %0,%%cr0" : : "r" (cr0));
23 }
24
25 asm volatile("fninit ; fnstsw %0 ; fnstcw %1"
26 : "+m" (fsw), "+m" (fcw));
27
28 return fsw == 0 && (fcw & 0x103f) == 0x003f;
29}
30
31int has_eflag(unsigned long mask)
32{
33 unsigned long f0, f1;
34
35 asm volatile("pushf \n\t"
36 "pushf \n\t"
37 "pop %0 \n\t"
38 "mov %0,%1 \n\t"
39 "xor %2,%1 \n\t"
40 "push %1 \n\t"
41 "popf \n\t"
42 "pushf \n\t"
43 "pop %1 \n\t"
44 "popf"
45 : "=&r" (f0), "=&r" (f1)
46 : "ri" (mask));
47
48 return !!((f0^f1) & mask);
49}
50
51/* Handle x86_32 PIC using ebx. */
52#if defined(__i386__) && defined(__PIC__)
53# define EBX_REG "=r"
54#else
55# define EBX_REG "=b"
56#endif
57
58static inline void cpuid(u32 id, u32 *a, u32 *b, u32 *c, u32 *d)
59{
60 asm volatile(".ifnc %%ebx,%3 ; movl %%ebx,%3 ; .endif \n\t"
61 "cpuid \n\t"
62 ".ifnc %%ebx,%3 ; xchgl %%ebx,%3 ; .endif \n\t"
63 : "=a" (*a), "=c" (*c), "=d" (*d), EBX_REG (*b)
64 : "a" (id)
65 );
66}
67
68void get_flags(void)
69{
70 u32 max_intel_level, max_amd_level;
71 u32 tfms;
72 u32 ignored;
73
74 if (loaded_flags)
75 return;
76 loaded_flags = true;
77
78 if (has_fpu())
79 set_bit(X86_FEATURE_FPU, cpu.flags);
80
81 if (has_eflag(X86_EFLAGS_ID)) {
82 cpuid(0x0, &max_intel_level, &cpu_vendor[0], &cpu_vendor[2],
83 &cpu_vendor[1]);
84
85 if (max_intel_level >= 0x00000001 &&
86 max_intel_level <= 0x0000ffff) {
87 cpuid(0x1, &tfms, &ignored, &cpu.flags[4],
88 &cpu.flags[0]);
89 cpu.level = (tfms >> 8) & 15;
90 cpu.model = (tfms >> 4) & 15;
91 if (cpu.level >= 6)
92 cpu.model += ((tfms >> 16) & 0xf) << 4;
93 }
94
95 cpuid(0x80000000, &max_amd_level, &ignored, &ignored,
96 &ignored);
97
98 if (max_amd_level >= 0x80000001 &&
99 max_amd_level <= 0x8000ffff) {
100 cpuid(0x80000001, &ignored, &ignored, &cpu.flags[6],
101 &cpu.flags[1]);
102 }
103 }
104}
diff --git a/arch/x86/boot/cpuflags.h b/arch/x86/boot/cpuflags.h
new file mode 100644
index 000000000000..9bb4e25f7317
--- /dev/null
+++ b/arch/x86/boot/cpuflags.h
@@ -0,0 +1,19 @@
1#ifndef BOOT_CPUFLAGS_H
2#define BOOT_CPUFLAGS_H
3
4#include <asm/cpufeature.h>
5#include <asm/processor-flags.h>
6
7struct cpu_features {
8 int level; /* Family, or 64 for x86-64 */
9 int model;
10 u32 flags[NCAPINTS];
11};
12
13extern struct cpu_features cpu;
14extern u32 cpu_vendor[3];
15
16int has_eflag(unsigned long mask);
17void get_flags(void);
18
19#endif