[PATCH 12/12] i386: remove cpuid checking in head.S

From: Eric W. Biederman
Date: Mon Apr 30 2007 - 12:36:49 EST



This patch augments the existing cpu initialization code in C
with the the few missing pieces that were performed in assembly.
Allowing us to remove cpu initialization from head.S completely.

This should also allow remove the need to call cpu_detect in
the paravirt initialization code paths.

Signed-off-by: Eric W. Biederman <ebiederm@xxxxxxxxxxxx>
---
arch/i386/kernel/cpu/common.c | 55 +++++++++++++++++++----
arch/i386/kernel/head.S | 96 +----------------------------------------
arch/i386/kernel/setup.c | 3 -
3 files changed, 47 insertions(+), 107 deletions(-)

diff --git a/arch/i386/kernel/cpu/common.c b/arch/i386/kernel/cpu/common.c
index 794d593..1a7b48a 100644
--- a/arch/i386/kernel/cpu/common.c
+++ b/arch/i386/kernel/cpu/common.c
@@ -251,6 +251,16 @@ static inline int flag_is_changeable_p(u32 flag)
return ((f1^f2) & flag) != 0;
}

+static inline int has_x87(void)
+{
+ unsigned result;
+ asm("clts\n\t"
+ "fninit\n\t"
+ "fstsw %%ax\n\t"
+ : "=a"(result));
+ return (result & 0xf) == 0;
+}
+

/* Probe for the CPUID instruction */
static int __cpuinit have_cpuid_p(void)
@@ -258,6 +268,23 @@ static int __cpuinit have_cpuid_p(void)
return flag_is_changeable_p(X86_EFLAGS_ID);
}

+static int __cpuinit init_cr0(void)
+{
+ unsigned long cr0;
+ int hard_math;
+ cr0 = read_cr0();
+ cr0 &= X86_CR0_PG | X86_CR0_ET | X86_CR0_PE;
+ cr0 |= X86_CR0_MP;
+ if (flag_is_changeable_p(X86_EFLAGS_AC))
+ cr0 |= X86_CR0_AM | X86_CR0_WP | X86_CR0_NE;
+ write_cr0(cr0);
+ hard_math = has_x87();
+ if (!hard_math)
+ /* No coprocessor: Enable emulation */
+ write_cr0(cr0 | X86_CR0_EM);
+ return hard_math;
+}
+
void __init cpu_detect(struct cpuinfo_x86 *c)
{
/* Get vendor name */
@@ -268,8 +295,10 @@ void __init cpu_detect(struct cpuinfo_x86 *c)

c->x86 = 4;
if (c->cpuid_level >= 0x00000001) {
- u32 junk, tfms, cap0, misc;
- cpuid(0x00000001, &tfms, &misc, &junk, &cap0);
+ u32 tfms, misc, excap, capability;
+ cpuid(0x00000001, &tfms, &misc, &excap, &capability);
+ c->x86_capability[0] = capability;
+ c->x86_capability[4] = excap;
c->x86 = (tfms >> 8) & 15;
c->x86_model = (tfms >> 4) & 15;
if (c->x86 == 0xf)
@@ -277,7 +306,7 @@ void __init cpu_detect(struct cpuinfo_x86 *c)
if (c->x86 >= 0x6)
c->x86_model += ((tfms >> 16) & 0xF) << 4;
c->x86_mask = tfms & 15;
- if (cap0 & (1<<19))
+ if (capability & (1<<19))
c->x86_cache_alignment = ((misc >> 8) & 0xff) * 8;
}
}
@@ -292,14 +321,21 @@ static void __init early_cpu_detect(void)
{
struct cpuinfo_x86 *c = &boot_cpu_data;

+ c->cpuid_level = -1;
c->x86_cache_alignment = 32;

- if (!have_cpuid_p())
- return;
-
- cpu_detect(c);
-
- get_cpu_vendor(c, 1);
+ if (!have_cpuid_p()) {
+ /* First of all, decide if this is a 486 or higher */
+ /* It's a 486 if we can modify the AC flag */
+ if ( flag_is_changeable_p(X86_EFLAGS_AC) )
+ c->x86 = 4;
+ else
+ c->x86 = 3;
+ } else {
+ cpu_detect(c);
+ get_cpu_vendor(c, 1);
+ }
+ c->hard_math = init_cr0();
}

static void __cpuinit generic_identify(struct cpuinfo_x86 * c)
@@ -670,6 +706,7 @@ void __cpuinit cpu_init(void)

printk(KERN_INFO "Initializing CPU#%d\n", cpu);

+ init_cr0();
if (cpu_has_vme || cpu_has_tsc || cpu_has_de)
clear_in_cr4(X86_CR4_VME|X86_CR4_PVI|X86_CR4_TSD|X86_CR4_DE);
if (tsc_disable && cpu_has_tsc) {
diff --git a/arch/i386/kernel/head.S b/arch/i386/kernel/head.S
index 0ee615b..5e3478b 100644
--- a/arch/i386/kernel/head.S
+++ b/arch/i386/kernel/head.S
@@ -20,19 +20,6 @@
#include <asm/setup.h>

/*
- * References to members of the new_cpu_data structure.
- */
-
-#define X86 new_cpu_data+CPUINFO_x86
-#define X86_VENDOR new_cpu_data+CPUINFO_x86_vendor
-#define X86_MODEL new_cpu_data+CPUINFO_x86_model
-#define X86_MASK new_cpu_data+CPUINFO_x86_mask
-#define X86_HARD_MATH new_cpu_data+CPUINFO_hard_math
-#define X86_CPUID new_cpu_data+CPUINFO_cpuid_level
-#define X86_CAPABILITY new_cpu_data+CPUINFO_x86_capability
-#define X86_VENDOR_ID new_cpu_data+CPUINFO_x86_vendor_id
-
-/*
* 32-bit kernel entrypoint; only used by the boot CPU. On entry,
* %esi points to the real-mode code as a 32-bit pointer.
* CS and DS must be 4 GB flat segments, but we don't depend on
@@ -182,6 +169,7 @@ ENTRY(startup_32_smp)
movl $swapper_pg_dir-__PAGE_OFFSET,%eax
movl %eax,%cr3 /* set the page table pointer.. */
movl %cr0,%eax
+ andl $0x0000011,%eax /* Save PE,ET */
orl $0x80000000,%eax
movl %eax,%cr0 /* ..and set paging (PG) bit */
ljmp $__BOOT_CS,$1f /* Clear prefetch and normalize %eip */
@@ -197,69 +185,6 @@ ENTRY(startup_32_smp)
pushl $0
popfl

-checkCPUtype:
-
- movl $-1,X86_CPUID # -1 for no CPUID initially
-
-/* check if it is 486 or 386. */
-/*
- * XXX - this does a lot of unnecessary setup. Alignment checks don't
- * apply at our cpl of 0 and the stack ought to be aligned already, and
- * we don't need to preserve eflags.
- */
-
- movb $3,X86 # at least 386
- pushfl # push EFLAGS
- popl %eax # get EFLAGS
- movl %eax,%ecx # save original EFLAGS
- xorl $0x240000,%eax # flip AC and ID bits in EFLAGS
- pushl %eax # copy to EFLAGS
- popfl # set EFLAGS
- pushfl # get new EFLAGS
- popl %eax # put it in eax
- xorl %ecx,%eax # change in flags
- pushl %ecx # restore original EFLAGS
- popfl
- testl $0x40000,%eax # check if AC bit changed
- je is386
-
- movb $4,X86 # at least 486
- testl $0x200000,%eax # check if ID bit changed
- je is486
-
- /* get vendor info */
- xorl %eax,%eax # call CPUID with 0 -> return vendor ID
- cpuid
- movl %eax,X86_CPUID # save CPUID level
- movl %ebx,X86_VENDOR_ID # lo 4 chars
- movl %edx,X86_VENDOR_ID+4 # next 4 chars
- movl %ecx,X86_VENDOR_ID+8 # last 4 chars
-
- orl %eax,%eax # do we have processor info as well?
- je is486
-
- movl $1,%eax # Use the CPUID instruction to get CPU type
- cpuid
- movb %al,%cl # save reg for future use
- andb $0x0f,%ah # mask processor family
- movb %ah,X86
- andb $0xf0,%al # mask model
- shrb $4,%al
- movb %al,X86_MODEL
- andb $0x0f,%cl # mask mask revision
- movb %cl,X86_MASK
- movl %edx,X86_CAPABILITY
-
-is486: movl $0x50022,%ecx # set AM, WP, NE and MP
- jmp 2f
-
-is386: movl $2,%ecx # set MP
-2: movl %cr0,%eax
- andl $0x80000011,%eax # Save PG,PE,ET
- orl %ecx,%eax
- movl %eax,%cr0
-
- call check_x87
lgdt early_gdt_descr
ljmp $(__KERNEL_CS),$1f
1: movl $(__KERNEL_DS),%eax # reload all the segment registers
@@ -288,25 +213,6 @@ is386: movl $2,%ecx # set MP
#endif /* CONFIG_SMP */
jmp i386_start_kernel

-/*
- * We depend on ET to be correct. This checks for 287/387.
- */
-check_x87:
- movb $0,X86_HARD_MATH
- clts
- fninit
- fstsw %ax
- cmpb $0,%al
- je 1f
- movl %cr0,%eax /* no coprocessor: have to set bits */
- xorl $4,%eax /* set EM */
- movl %eax,%cr0
- ret
- ALIGN
-1: movb $1,X86_HARD_MATH
- .byte 0xDB,0xE4 /* fsetpm for 287, ignored by 387 */
- ret
-
ENTRY(early_divide_err)
xor %edx,%edx
pushl $0 /* fake errcode */
diff --git a/arch/i386/kernel/setup.c b/arch/i386/kernel/setup.c
index 3e31591..401e48a 100644
--- a/arch/i386/kernel/setup.c
+++ b/arch/i386/kernel/setup.c
@@ -76,8 +76,6 @@ int disable_pse __devinitdata = 0;
extern struct resource code_resource;
extern struct resource data_resource;

-/* cpu data as detected by the assembly code in head.S */
-struct cpuinfo_x86 new_cpu_data __cpuinitdata = { 0, 0, 0, 0, -1, 1, 0, 0, -1 };
/* common cpu data for all cpus */
struct cpuinfo_x86 boot_cpu_data __read_mostly = { 0, 0, 0, 0, -1, 1, 0, 0, -1 };
EXPORT_SYMBOL(boot_cpu_data);
@@ -515,7 +513,6 @@ void __init setup_arch(char **cmdline_p)
{
unsigned long max_low_pfn;

- memcpy(&boot_cpu_data, &new_cpu_data, sizeof(new_cpu_data));
pre_setup_arch_hook();
early_cpu_init();

--
1.5.1.1.181.g2de0

-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/