aboutsummaryrefslogtreecommitdiff
path: root/src/arch/i386/lib
diff options
context:
space:
mode:
Diffstat (limited to 'src/arch/i386/lib')
-rw-r--r--src/arch/i386/lib/c_start.S30
-rw-r--r--src/arch/i386/lib/console.c11
-rw-r--r--src/arch/i386/lib/console.inc12
-rw-r--r--src/arch/i386/lib/cpu.c355
-rw-r--r--src/arch/i386/lib/id.inc11
5 files changed, 246 insertions, 173 deletions
diff --git a/src/arch/i386/lib/c_start.S b/src/arch/i386/lib/c_start.S
index 47fb64588c..c82da2f76e 100644
--- a/src/arch/i386/lib/c_start.S
+++ b/src/arch/i386/lib/c_start.S
@@ -1,8 +1,5 @@
#include <arch/asm.h>
#include <arch/intel.h>
-#if CONFIG_SMP==1
-#include <cpu/p6/apic.h>
-#endif
.section ".text"
.code32
.globl _start
@@ -39,27 +36,10 @@ _start:
/* set new stack */
movl $_estack, %esp
-#if CONFIG_SMP==1
- /* Get the cpu id */
- movl $APIC_DEFAULT_BASE, %edi
- movl APIC_ID(%edi), %eax
- shrl $24, %eax
-
- /* Get the cpu index (CONFIG_MAX_CPUS on error) */
- movl $-4, %ebx
-1: addl $4, %ebx
- cmpl $(CONFIG_MAX_CPUS << 2), %ebx
- je 2
- cmpl %eax, initial_apicid(%ebx)
- jne 1b
-2: shrl $2, %ebx
-
- /* Now compute the appropriate stack */
- movl %ebx, %eax
- movl $STACK_SIZE, %ebx
- mull %ebx
- subl %eax, %esp
-#endif
+
+ /* Push the cpu index and struct cpu */
+ pushl $0
+ pushl $0
/* push the boot_complete flag */
pushl %ebp
@@ -74,7 +54,7 @@ _start:
*/
intel_chip_post_macro(0xfe) /* post fe */
- /* Resort the stack location */
+ /* Restore the stack location */
movl %ebp, %esp
/* The boot_complete flag has already been pushed */
diff --git a/src/arch/i386/lib/console.c b/src/arch/i386/lib/console.c
index f3594e9786..76f4f16e01 100644
--- a/src/arch/i386/lib/console.c
+++ b/src/arch/i386/lib/console.c
@@ -118,21 +118,18 @@ static void print_spew_hex16(unsigned short value){ __console_tx_hex16(BIOS_SPEW
static void print_spew_hex32(unsigned int value) { __console_tx_hex32(BIOS_SPEW, value); }
static void print_spew(const char *str) { __console_tx_string(BIOS_SPEW, str); }
-#define __STR(X) #X
-#define STR(X) __STR(X)
-
#ifndef LINUXBIOS_EXTRA_VERSION
-#define LINUXBIOS_EXTRA_VERSION
+#define LINUXBIOS_EXTRA_VERSION ""
#endif
static void console_init(void)
{
static const char console_test[] =
"\r\n\r\nLinuxBIOS-"
- STR(LINUXBIOS_VERSION)
- STR(LINUXBIOS_EXTRA_VERSION)
+ LINUXBIOS_VERSION
+ LINUXBIOS_EXTRA_VERSION
" "
- STR(LINUXBIOS_BUILD)
+ LINUXBIOS_BUILD
" starting...\r\n";
print_info(console_test);
}
diff --git a/src/arch/i386/lib/console.inc b/src/arch/i386/lib/console.inc
index b617b8c1d0..be7df6af16 100644
--- a/src/arch/i386/lib/console.inc
+++ b/src/arch/i386/lib/console.inc
@@ -2,11 +2,8 @@
jmp console0
-#define __STR(X) #X
-#define STR(X) __STR(X)
-
#ifndef LINUXBIOS_EXTRA_VERSION
-#define LINUXBIOS_EXTRA_VERSION
+#define LINUXBIOS_EXTRA_VERSION ""
#endif
#ifndef ASM_CONSOLE_LOGLEVEL
@@ -14,13 +11,12 @@ jmp console0
#endif
console_test:
.ascii "\r\n\r\nLinuxBIOS-"
- .ascii STR(LINUXBIOS_VERSION)
- .ascii STR(LINUXBIOS_EXTRA_VERSION)
+ .ascii LINUXBIOS_VERSION
+ .ascii LINUXBIOS_EXTRA_VERSION
.ascii " "
- .ascii STR(LINUXBIOS_BUILD)
+ .ascii LINUXBIOS_BUILD
.asciz " starting...\r\n"
-#undef STR
/* uses: ax, dx */
#if CONFIG_CONSOLE_SERIAL8250
#define __CONSOLE_INLINE_TX_AL TTYS0_TX_AL
diff --git a/src/arch/i386/lib/cpu.c b/src/arch/i386/lib/cpu.c
index 23c35bc816..74db2e81f8 100644
--- a/src/arch/i386/lib/cpu.c
+++ b/src/arch/i386/lib/cpu.c
@@ -1,147 +1,252 @@
#include <console/console.h>
#include <cpu/cpu.h>
-#include <mem.h>
#include <arch/io.h>
#include <string.h>
-#include <cpu/cpufixup.h>
-#include <smp/start_stop.h>
-#include <cpu/cpufixup.h>
-#include <cpu/p6/mtrr.h>
-#include <cpu/p6/msr.h>
-#include <cpu/p6/apic.h>
-#include <cpu/p5/cpuid.h>
-#include <arch/smp/lapic.h>
-#if 0
-#include <cpu/l2_cache.h>
-#endif
-
-#if CONFIG_SMP || CONFIG_IOAPIC
-#define APIC 1
-#endif
-
-
-static void cache_on(struct mem_range *mem)
+#include <cpu/x86/mtrr.h>
+#include <cpu/x86/msr.h>
+#include <cpu/x86/lapic.h>
+#include <arch/cpu.h>
+#include <device/path.h>
+#include <device/device.h>
+#include <smp/spinlock.h>
+
+/* Standard macro to see if a specific flag is changeable */
+static inline int flag_is_changeable_p(uint32_t flag)
{
- post_code(0x60);
- printk_info("Enabling cache...");
-
-
- /* we need an #ifdef i586 here at some point ... */
- __asm__ __volatile__("mov %cr0, %eax\n\t"
- "and $0x9fffffff,%eax\n\t"
- "mov %eax, %cr0\n\t");
- /* turns out cache isn't really on until you set MTRR registers on
- * 686 and later.
- * NOTHING FANCY. Linux does a much better job anyway.
- * so absolute minimum needed to get it going.
- */
- /* OK, linux it turns out does nothing. We have to do it ... */
-#if i686==1
- // totalram here is in linux sizing, i.e. units of KB.
- // set_mtrr is responsible for getting it into the right units!
- setup_mtrrs(mem);
-#endif
-
- post_code(0x6A);
- printk_info("done.\n");
+ uint32_t f1, f2;
+
+ asm(
+ "pushfl\n\t"
+ "pushfl\n\t"
+ "popl %0\n\t"
+ "movl %0,%1\n\t"
+ "xorl %2,%0\n\t"
+ "pushl %0\n\t"
+ "popfl\n\t"
+ "pushfl\n\t"
+ "popl %0\n\t"
+ "popfl\n\t"
+ : "=&r" (f1), "=&r" (f2)
+ : "ir" (flag));
+ return ((f1^f2) & flag) != 0;
}
-static void interrupts_on()
+
+/* Probe for the CPUID instruction */
+static int have_cpuid_p(void)
{
- /* this is so interrupts work. This is very limited scope --
- * linux will do better later, we hope ...
- */
- /* this is the first way we learned to do it. It fails on real SMP
- * stuff. So we have to do things differently ...
- * see the Intel mp1.4 spec, page A-3
- */
+ return flag_is_changeable_p(X86_EFLAGS_ID);
+}
+
+/*
+ * Cyrix CPUs without cpuid or with cpuid not yet enabled can be detected
+ * by the fact that they preserve the flags across the division of 5/2.
+ * PII and PPro exhibit this behavior too, but they have cpuid available.
+ */
+
+/*
+ * Perform the Cyrix 5/2 test. A Cyrix won't change
+ * the flags, while other 486 chips will.
+ */
+static inline int test_cyrix_52div(void)
+{
+ unsigned int test;
+
+ __asm__ __volatile__(
+ "sahf\n\t" /* clear flags (%eax = 0x0005) */
+ "div %b2\n\t" /* divide 5 by 2 */
+ "lahf" /* store flags into %ah */
+ : "=a" (test)
+ : "0" (5), "q" (2)
+ : "cc");
+
+ /* AH is 0x02 on Cyrix after the divide.. */
+ return (unsigned char) (test >> 8) == 0x02;
+}
-#if defined(APIC)
- /* Only Pentium Pro and later have those MSR stuff */
- msr_t msr;
+/*
+ * Detect a NexGen CPU running without BIOS hypercode new enough
+ * to have CPUID. (Thanks to Herbert Oppmann)
+ */
+
+static int deep_magic_nexgen_probe(void)
+{
+ int ret;
+
+ __asm__ __volatile__ (
+ " movw $0x5555, %%ax\n"
+ " xorw %%dx,%%dx\n"
+ " movw $2, %%cx\n"
+ " divw %%cx\n"
+ " movl $0, %%eax\n"
+ " jnz 1f\n"
+ " movl $1, %%eax\n"
+ "1:\n"
+ : "=a" (ret) : : "cx", "dx" );
+ return ret;
+}
- printk_info("Setting up local apic...");
+/* List of cpu vendor strings along with their normalized
+ * id values.
+ */
+static struct {
+ int vendor;
+ const char *name;
+} x86_vendors[] = {
+ { X86_VENDOR_INTEL, "GenuineIntel", },
+ { X86_VENDOR_CYRIX, "CyrixInstead", },
+ { X86_VENDOR_AMD, "AuthenticAMD", },
+ { X86_VENDOR_UMC, "UMC UMC UMC ", },
+ { X86_VENDOR_NEXGEN, "NexGenDriven", },
+ { X86_VENDOR_CENTAUR, "CentaurHauls", },
+ { X86_VENDOR_RISE, "RiseRiseRise", },
+ { X86_VENDOR_TRANSMETA, "GenuineTMx86", },
+ { X86_VENDOR_TRANSMETA, "TransmetaCPU", },
+ { X86_VENDOR_NSC, "Geode by NSC", },
+ { X86_VENDOR_SIS, "SiS SiS SiS ", },
+};
+
+static const char *x86_vendor_name[] = {
+ [X86_VENDOR_INTEL] = "Intel",
+ [X86_VENDOR_CYRIX] = "Cyrix",
+ [X86_VENDOR_AMD] = "AMD",
+ [X86_VENDOR_UMC] = "UMC",
+ [X86_VENDOR_NEXGEN] = "NexGen",
+ [X86_VENDOR_CENTAUR] = "Centaur",
+ [X86_VENDOR_RISE] = "Rise",
+ [X86_VENDOR_TRANSMETA] = "Transmeta",
+ [X86_VENDOR_NSC] = "NSC",
+ [X86_VENDOR_SIS] = "SiS",
+};
+
+static const char *cpu_vendor_name(int vendor)
+{
+ const char *name;
+ name = "<invalid cpu vendor>";
+ if ((vendor < (sizeof(x86_vendor_name)/sizeof(x86_vendor_name[0]))) &&
+ (x86_vendor_name[vendor] != 0))
+ {
+ name = x86_vendor_name[vendor];
+ }
+ return name;
+}
- /* Enable the local apic */
- msr = rdmsr(APIC_BASE_MSR);
- msr.lo |= APIC_BASE_MSR_ENABLE;
- msr.lo &= ~APIC_BASE_MSR_ADDR_MASK;
- msr.lo |= APIC_DEFAULT_BASE;
- wrmsr(APIC_BASE_MSR, msr);
+static void identify_cpu(struct device *cpu)
+{
+ char vendor_name[16];
+ int cpuid_level;
+ int i;
+
+ vendor_name[0] = '\0'; /* Unset */
+ cpuid_level = -1; /* Maximum supported CPUID level, -1=no CPUID */
+
+ /* Find the id and vendor_name */
+ if (!have_cpuid_p()) {
+ /* Its a 486 if we can modify the AC flag */
+ if (flag_is_changeable_p(X86_EFLAGS_AC)) {
+ cpu->device = 0x00000400; /* 486 */
+ } else {
+ cpu->device = 0x00000300; /* 386 */
+ }
+ if ((cpu->device == 0x00000400) && test_cyrix_52div()) {
+ memcpy(vendor_name, "CyrixInstead", 13);
+ /* If we ever care we can enable cpuid here */
+ }
+ /* Detect NexGen with old hypercode */
+ else if (deep_magic_nexgen_probe()) {
+ memcpy(vendor_name, "NexGenDriven", 13);
+ }
+ }
+ if (have_cpuid_p()) {
+ struct cpuid_result result;
+ result = cpuid(0x00000000);
+ cpuid_level = result.eax;
+ vendor_name[ 0] = (result.ebx >> 0) & 0xff;
+ vendor_name[ 1] = (result.ebx >> 8) & 0xff;
+ vendor_name[ 2] = (result.ebx >> 16) & 0xff;
+ vendor_name[ 3] = (result.ebx >> 24) & 0xff;
+ vendor_name[ 4] = (result.edx >> 0) & 0xff;
+ vendor_name[ 5] = (result.edx >> 8) & 0xff;
+ vendor_name[ 6] = (result.edx >> 16) & 0xff;
+ vendor_name[ 7] = (result.edx >> 24) & 0xff;
+ vendor_name[ 8] = (result.ecx >> 0) & 0xff;
+ vendor_name[ 9] = (result.ecx >> 8) & 0xff;
+ vendor_name[10] = (result.ecx >> 16) & 0xff;
+ vendor_name[11] = (result.ecx >> 24) & 0xff;
+ vendor_name[12] = '\0';
+
+ /* Intel-defined flags: level 0x00000001 */
+ if (cpuid_level >= 0x00000001) {
+ cpu->device = cpuid_eax(0x00000001);
+ }
+ else {
+ /* Have CPUID level 0 only unheard of */
+ cpu->device = 0x00000400;
+ }
+ }
+ cpu->vendor = X86_VENDOR_UNKNOWN;
+ for(i = 0; i < sizeof(x86_vendors)/sizeof(x86_vendors[0]); i++) {
+ if (memcmp(vendor_name, x86_vendors[i].name, 12) == 0) {
+ cpu->vendor = x86_vendors[i].vendor;
+ break;
+ }
+ }
+}
- /*
- * Set Task Priority to 'accept all'.
- */
- apic_write_around(APIC_TASKPRI,
- apic_read_around(APIC_TASKPRI) & ~APIC_TPRI_MASK);
-
- /* Put the local apic in virtual wire mode */
- apic_write_around(APIC_SPIV,
- (apic_read_around(APIC_SPIV) & ~(APIC_VECTOR_MASK))
- | APIC_SPIV_ENABLE);
- apic_write_around(APIC_LVT0,
- (apic_read_around(APIC_LVT0) &
- ~(APIC_LVT_MASKED | APIC_LVT_LEVEL_TRIGGER |
- APIC_LVT_REMOTE_IRR | APIC_INPUT_POLARITY |
- APIC_SEND_PENDING |APIC_LVT_RESERVED_1 |
- APIC_DELIVERY_MODE_MASK))
- | (APIC_LVT_REMOTE_IRR |APIC_SEND_PENDING |
- APIC_DELIVERY_MODE_EXTINT)
- );
- apic_write_around(APIC_LVT1,
- (apic_read_around(APIC_LVT1) &
- ~(APIC_LVT_MASKED | APIC_LVT_LEVEL_TRIGGER |
- APIC_LVT_REMOTE_IRR | APIC_INPUT_POLARITY |
- APIC_SEND_PENDING |APIC_LVT_RESERVED_1 |
- APIC_DELIVERY_MODE_MASK))
- | (APIC_LVT_REMOTE_IRR |APIC_SEND_PENDING |
- APIC_DELIVERY_MODE_NMI)
- );
-
- printk_debug(" apic_id: %d ", lapicid());
-
-#else /* APIC */
-#if i686==1
- /* Only Pentium Pro and later have those MSR stuff */
- msr_t msr;
-
- printk_info("Disabling local apic...");
-
- msr = rdmsr(APIC_BASE_MSR);
- msr.lo &= ~APIC_BASE_MSR_ENABLE;
- wrmsr(APIC_BASE_MSR, msr);
-#endif /* i686 */
-#endif /* APIC */
- printk_info("done.\n");
- post_code(0x9b);
+static void set_cpu_ops(struct device *cpu)
+{
+ struct cpu_driver *driver;
+ cpu->ops = 0;
+ for (driver = cpu_drivers; driver < ecpu_drivers; driver++) {
+ struct cpu_device_id *id;
+ for(id = driver->id_table; id->vendor != X86_VENDOR_INVALID; id++) {
+ if ((cpu->vendor == id->vendor) &&
+ (cpu->device == id->device))
+ {
+ goto found;
+ }
+ }
+ }
+ die("Unknown cpu");
+ return;
+ found:
+ cpu->ops = driver->ops;
}
-unsigned long cpu_initialize(struct mem_range *mem)
+void cpu_initialize(void)
{
/* Because we busy wait at the printk spinlock.
* It is important to keep the number of printed messages
* from secondary cpus to a minimum, when debugging is
* disabled.
*/
- unsigned long processor_id = this_processors_id();
- printk_notice("Initializing CPU #%d\n", processor_id);
-
- /* Turn on caching if we haven't already */
- cache_on(mem);
-#if i586==1
- display_cpuid();
-#endif
-#if i686==1
- mtrr_check();
-#endif
-
- /* some cpus need a fixup done. This is the hook for doing that. */
- cpufixup(mem);
-
- interrupts_on();
-
- processor_id = this_processors_id();
- printk_info("CPU #%d Initialized\n", processor_id);
- return processor_id;
+ struct device *cpu;
+ struct cpu_info *info;
+ info = cpu_info();
+
+ printk_notice("Initializing CPU #%d\n", info->index);
+
+ cpu = info->cpu;
+ if (!cpu) {
+ die("CPU: missing cpu device structure");
+ }
+
+ /* Find what type of cpu we are dealing with */
+ identify_cpu(cpu);
+ printk_debug("CPU: vendor %s device %x\n",
+ cpu_vendor_name(cpu->vendor), cpu->device);
+
+ /* Lookup the cpu's operations */
+ set_cpu_ops(cpu);
+
+ /* Initialize the cpu */
+ if (cpu->ops && cpu->ops->init) {
+ cpu->enabled = 1;
+ cpu->initialized = 1;
+ cpu->ops->init(cpu);
+ }
+
+ printk_info("CPU #%d Initialized\n", info->index);
+ return;
}
diff --git a/src/arch/i386/lib/id.inc b/src/arch/i386/lib/id.inc
index f28e23a2e8..44c452b03a 100644
--- a/src/arch/i386/lib/id.inc
+++ b/src/arch/i386/lib/id.inc
@@ -1,21 +1,16 @@
- .section ".id", "a", @progbits
-#define __STR(X) #X
-#define STR(X) __STR(X)
+ .section ".id", "a", @progbits
.globl __id_start
__id_start:
vendor:
- .asciz STR(MAINBOARD_VENDOR)
+ .asciz MAINBOARD_VENDOR
part:
- .asciz STR(MAINBOARD_PART_NUMBER)
+ .asciz MAINBOARD_PART_NUMBER
.long __id_end + 0x10 - vendor /* Reverse offset to the vendor id */
.long __id_end + 0x10 - part /* Reverse offset to the part number */
.long PAYLOAD_SIZE + ROM_IMAGE_SIZE /* Size of this romimage */
.globl __id_end
-#undef __STR
-#undef STR
-
__id_end:
.previous