patch-1.3.65 linux/arch/i386/kernel/smp.c
Next file: linux/drivers/block/floppy.c
Previous file: linux/arch/i386/kernel/signal.c
Back to the patch index
Back to the overall index
- Lines: 458
- Date:
Sat Feb 17 08:07:01 1996
- Orig file:
v1.3.64/linux/arch/i386/kernel/smp.c
- Orig date:
Wed Feb 7 15:11:21 1996
diff -u --recursive --new-file v1.3.64/linux/arch/i386/kernel/smp.c linux/arch/i386/kernel/smp.c
@@ -1,5 +1,5 @@
/*
- * Intel MP v1.1 specification support routines for multi-pentium
+ * Intel MP v1.1/v1.4 specification support routines for multi-pentium
* hosts.
*
* (c) 1995 Alan Cox, CymruNET Ltd <alan@cymru.net>
@@ -7,6 +7,7 @@
* Much of the core SMP work is based on previous work by Thomas Radke, to
* whom a great many thanks are extended.
*
+ *
* This code is released under the GNU public license version 2 or
* later.
*
@@ -15,7 +16,7 @@
* Jose Renau : Handle single CPU case.
* Alan Cox : By repeated request 8) - Total BogoMIP report.
* Greg Wright : Fix for kernel stacks panic.
- *
+ * Erich Boleyn : MP v1.4 and additional changes.
*/
#include <linux/kernel.h>
@@ -33,13 +34,12 @@
#include <asm/pgtable.h>
#include <asm/smp.h>
-extern void *vremap(unsigned long offset, unsigned long size); /* Linus hasnt put this in the headers yet */
-
static int smp_found_config=0; /* Have we found an SMP box */
unsigned long cpu_present_map = 0; /* Bitmask of existing CPU's */
int smp_num_cpus; /* Total count of live CPU's */
int smp_threads_ready=0; /* Set when the idlers are all forked */
+volatile unsigned long cpu_number_map[NR_CPUS]; /* which CPU maps to which logical number */
volatile unsigned long cpu_callin_map[NR_CPUS] = {0,}; /* We always use 0 the rest is ready for parallel delivery */
volatile unsigned long smp_invalidate_needed; /* Used for the invalidate map thats also checked in the spinlock */
struct cpuinfo_x86 cpu_data[NR_CPUS]; /* Per cpu bogomips and other parameters */
@@ -49,8 +49,9 @@
static unsigned char *kstack_base,*kstack_end; /* Kernel stack list pointers */
static int smp_activated = 0; /* Tripped once we need to start cross invalidating */
static volatile int smp_commenced=0; /* Tripped when we start scheduling */
-static unsigned char nlong=0; /* Apparent value for boot CPU */
-unsigned char *apic_reg=((unsigned char *)&nlong)-0x20; /* Later set to the vremap() of the APIC */
+unsigned long apic_addr=0xFEE00000; /* Address of APIC (defaults to 0xFEE00000) */
+unsigned long nlong = 0; /* dummy used for apic_reg address + 0x20 */
+unsigned char *apic_reg=((unsigned char *)(&nlong))-0x20;/* Later set to the vremap() of the APIC */
unsigned long apic_retval; /* Just debugging the assembler.. */
unsigned char *kernel_stacks[NR_CPUS]; /* Kernel stack pointers for CPU's (debugging) */
@@ -76,6 +77,14 @@
volatile unsigned long smp_idle_map=0; /* Map for idle processors */
#endif
+/*#define SMP_DEBUG*/
+
+#ifdef SMP_DEBUG
+#define SMP_PRINTK(x) printk x
+#else
+#define SMP_PRINTK(x)
+#endif
+
/*
* Checksum an MP configuration block.
@@ -139,9 +148,9 @@
printk("Checksum error.\n");
return 1;
}
- if(mpc->mpc_spec!=0x01)
+ if(mpc->mpc_spec!=0x01 && mpc->mpc_spec!=0x04)
{
- printk("Unsupported version (%d)\n",mpc->mpc_spec);
+ printk("Bad Config Table version (%d)!!\n",mpc->mpc_spec);
return 1;
}
memcpy(str,mpc->mpc_oem,8);
@@ -151,6 +160,9 @@
str[12]=0;
printk("Product ID: %s ",str);
printk("APIC at: 0x%lX\n",mpc->mpc_lapic);
+
+ /* set the local APIC address */
+ apic_addr = mpc->mpc_lapic;
/*
* Now process the configuration blocks.
@@ -173,6 +185,7 @@
(m->mpc_cpufeature&
CPU_MODEL_MASK)>>4),
m->mpc_apicver);
+#ifdef SMP_DEBUG
if(m->mpc_featureflag&(1<<0))
printk(" Floating point unit present.\n");
if(m->mpc_featureflag&(1<<7))
@@ -181,9 +194,10 @@
printk(" 64 bit compare & exchange supported.\n");
if(m->mpc_featureflag&(1<<9))
printk(" Internal APIC present.\n");
+#endif
if(m->mpc_cpuflag&CPU_BOOTPROCESSOR)
{
- printk(" Bootup CPU\n");
+ SMP_PRINTK((" Bootup CPU\n"));
boot_cpu_id=m->mpc_apicid;
nlong = boot_cpu_id<<24; /* Dummy 'self' for bootup */
}
@@ -205,9 +219,9 @@
(struct mpc_config_bus *)mpt;
memcpy(str,m->mpc_bustype,6);
str[6]=0;
- printk("Bus #%d is %s\n",
+ SMP_PRINTK(("Bus #%d is %s\n",
m->mpc_busid,
- str);
+ str));
mpt+=sizeof(*m);
count+=sizeof(*m);
break;
@@ -261,8 +275,8 @@
unsigned long *bp=(unsigned long *)base;
struct intel_mp_floating *mpf;
-/* printk("Scan SMP from %p for %ld bytes.\n",
- bp,length);*/
+ SMP_PRINTK(("Scan SMP from %p for %ld bytes.\n",
+ bp,length));
if(sizeof(*mpf)!=16)
printk("Error: MPF size\n");
@@ -273,11 +287,14 @@
mpf=(struct intel_mp_floating *)bp;
if(mpf->mpf_length==1 &&
!mpf_checksum((unsigned char *)bp,16) &&
- mpf->mpf_specification==1)
+ (mpf->mpf_specification == 1
+ || mpf->mpf_specification == 4) )
{
- printk("Intel multiprocessing (MPv1.1) available.\n");
+ printk("Intel MultiProcessor Specification v1.%d\n", mpf->mpf_specification);
if(mpf->mpf_feature2&(1<<7))
- printk(" IMCR and PIC mode supported.\n");
+ printk(" IMCR and PIC compatibility mode.\n");
+ else
+ printk(" Virtual Wire compatibility mode.\n");
smp_found_config=1;
/*
* Now see if we need to read further.
@@ -436,7 +453,7 @@
* Activate our APIC
*/
-/* printk("CALLIN %d\n",smp_processor_id());*/
+ SMP_PRINTK(("CALLIN %d\n",smp_processor_id()));
l=apic_read(APIC_SPIV);
l|=(1<<8); /* Enable */
apic_write(APIC_SPIV,l);
@@ -461,11 +478,12 @@
*(long *)0=1; OOPS... */
local_invalidate();
while(!smp_commenced);
+ if (cpu_number_map[cpuid] == -1)
+ while(1);
local_invalidate();
-/* printk("Commenced..\n");*/
+ SMP_PRINTK(("Commenced..\n"));
- /* This assumes the processor id's are consecutive 0..n-1 - FIXME */
- load_TR(cpuid);
+ load_TR(cpu_number_map[cpuid]);
/* while(1);*/
}
@@ -492,22 +510,42 @@
if(apic_reg == NULL)
panic("Unable to map local apic.\n");
+#ifdef SMP_DEBUG
+ {
+ int reg;
+
+ reg = apic_read(APIC_VERSION);
+ printk("Getting VERSION: %x\n", reg);
+
+ apic_write(APIC_VERSION, 0);
+ reg = apic_read(APIC_VERSION);
+ printk("Getting VERSION: %x\n", reg);
+
+ reg = apic_read(APIC_LVT0);
+ printk("Getting LVT0: %x\n", reg);
+
+ reg = apic_read(APIC_LVT1);
+ printk("Getting LVT1: %x\n", reg);
+ }
+#endif
+
/*
* Now scan the cpu present map and fire up anything we find.
*/
-
kernel_stacks[boot_cpu_id]=(void *)init_user_stack; /* Set up for boot processor first */
smp_store_cpu_info(boot_cpu_id); /* Final full version of the data */
active_kernel_processor=boot_cpu_id;
+
+ SMP_PRINTK(("CPU map: %lx\n", cpu_present_map));
for(i=0;i<NR_CPUS;i++)
{
if((cpu_present_map&(1<<i)) && i!=boot_cpu_id) /* Rebooting yourself is a bad move */
{
- unsigned long cfg;
+ unsigned long cfg, send_status, accept_status;
int timeout;
/*
@@ -519,7 +557,7 @@
panic("No memory for processor stacks.\n");
kernel_stacks[i]=stack;
install_trampoline(stack);
-
+
printk("Booting processor %d stack %p: ",i,stack); /* So we set whats up */
/*
@@ -530,65 +568,181 @@
cfg|=(1<<8); /* Enable APIC */
apic_write(APIC_SPIV,cfg);
- for(timeout=0;timeout<50000;timeout++)
+ /*
+ * This gunge runs the startup process for
+ * the targeted processor.
+ */
+
+#ifdef EEK
+ SMP_PRINTK(("Setting warm reset code and vector.\n"));
+
+ CMOS_WRITE(0xa, 0xf);
+ *((volatile unsigned short *) 0x467) = (unsigned short)(stack>>4);
+ *((volatile unsigned short *) 0x469) = 0;
+#endif
+
+ apic_write(APIC_ESR, 0);
+ accept_status = (apic_read(APIC_ESR) & 0xEF);
+ send_status = 0;
+ accept_status = 0;
+
+ SMP_PRINTK(("Asserting INIT.\n"));
+
+ cfg=apic_read(APIC_ICR2);
+ cfg&=0x00FFFFFF;
+ apic_write(APIC_ICR2, cfg|SET_APIC_DEST_FIELD(i)); /* Target chip */
+ cfg=apic_read(APIC_ICR);
+ cfg&=~0xCDFFF; /* Clear bits */
+ cfg|=0x0000c500; /* Urgh.. fix for constants */
+ apic_write(APIC_ICR, cfg); /* Send IPI */
+
+ timeout = 0;
+ do {
+ udelay(1000);
+ if ((send_status = (!(apic_read(APIC_ICR) & 0x00001000))))
+ break;
+ } while (timeout++ < 1000);
+
+#ifdef EEK2
+ if (send_status) {
+ apic_write(APIC_ESR, 0);
+ accept_status = (apic_read(APIC_ESR) & 0xEF);
+ }
+#endif
+
+ if (send_status && !accept_status)
{
- /*
- * This gunge sends an IPI (Inter Processor Interrupt) to the
- * processor we wish to wake. When the startup IPI is received
- * the target CPU does a real mode jump to the stack base.
- *
- * We do the following
- *
- * Time 0 : Send a STARTUP IPI (This is all that is needed).
- * Time 20000 : Send an INIT IPI for broken boards.
- * Time 20001 : Send a second STARTUP IPI for broken boards.
- *
- * We can't just do INIT/STARTUP - that breaks the correctly
- * implemented ASUS boards.
- */
+ SMP_PRINTK(("Deasserting INIT.\n"));
- if(timeout==20000)
- {
- cfg=apic_read(APIC_ICR2);
- cfg&=0x00FFFFFF;
- apic_write(APIC_ICR2, cfg|SET_APIC_DEST_FIELD(i)); /* Target chip */
- cfg=apic_read(APIC_ICR);
- cfg&=~0xFDFFF ; /* Clear bits */
- cfg|=APIC_DEST_DM_INIT; /* INIT the CPU */
- apic_write(APIC_ICR, cfg); /* Kick the second */
- printk("\nBuggy motherboard ?, trying an INIT IPI: ");
- udelay(10); /* Masses of time */
+ cfg=apic_read(APIC_ICR2);
+ cfg&=0x00FFFFFF;
+ apic_write(APIC_ICR2, cfg|SET_APIC_DEST_FIELD(i)); /* Target chip */
+ cfg=apic_read(APIC_ICR);
+ cfg&=~0xCDFFF; /* Clear bits */
+ cfg|=0x00008500;
+ apic_write(APIC_ICR, cfg); /* Send IPI */
+
+ timeout = 0;
+ do {
+ udelay(1000);
+ if ((send_status = !(apic_read(APIC_ICR) & 0x00001000) ))
+ break;
+ } while (timeout++ < 1000);
+
+ if (send_status) {
+ udelay(1000000);
+ apic_write(APIC_ESR, 0);
+ accept_status = (apic_read(APIC_ESR) & 0xEF);
}
- if(timeout==0||timeout==20001)
- {
- cfg=apic_read(APIC_ICR);
- cfg&=~0xFDFFF ; /* Clear bits */
- cfg|=APIC_DEST_FIELD|APIC_DEST_DM_STARTUP|(((unsigned long)stack)>>12); /* Boot on the stack */
- apic_write(APIC_ICR, cfg); /* Kick the second */
- udelay(10); /* Masses of time */
- cfg=apic_read(APIC_ESR);
- if(cfg&4) /* Send accept error */
- printk("Processor refused startup request.\n");
+ }
+
+ /*
+ * We currently assume an integrated
+ * APIC only, so STARTUP IPIs must be
+ * sent as well.
+ */
+
+ if (send_status && !accept_status)
+ {
+ SMP_PRINTK(("Sending first STARTUP.\n"));
+
+ /*
+ * First STARTUP IPI
+ */
+
+ cfg=apic_read(APIC_ICR2);
+ cfg&=0x00FFFFFF;
+ apic_write(APIC_ICR2, cfg|SET_APIC_DEST_FIELD(i)); /* Target chip */
+ cfg=apic_read(APIC_ICR);
+ cfg&=~0xCDFFF ; /* Clear bits */
+ cfg|=APIC_DEST_FIELD|APIC_DEST_DM_STARTUP|(((unsigned long)stack)>>12); /* Boot on the stack */
+ apic_write(APIC_ICR, cfg); /* Kick the second */
+
+ timeout = 0;
+ do {
+ udelay(1000);
+ if ((send_status = !(apic_read(APIC_ICR) & 0x00001000)) )
+ break;
+ } while (timeout++ < 1000);
+
+ if (send_status) {
+ udelay(1000000);
+ apic_write(APIC_ESR, 0);
+ accept_status = (apic_read(APIC_ESR) & 0xEF);
}
- if(cpu_callin_map[0]&(1<<i))
- break; /* It has booted */
- udelay(100); /* Wait 5s total for a response */
}
- if(cpu_callin_map[0]&(1<<i))
- cpucount++;
- else
+
+ if (send_status && !accept_status)
{
+ SMP_PRINTK(("Sending second STARTUP.\n"));
+
/*
- * At this point we should set up a BIOS warm start and try
- * a RESTART IPI. The 486+82489 MP pair don't support STARTUP IPI's
+ * Second STARTUP IPI
*/
- if(*((unsigned char *)8192)==0xA5)
- printk("Stuck ??\n");
+
+ cfg=apic_read(APIC_ICR2);
+ cfg&=0x00FFFFFF;
+ apic_write(APIC_ICR2, cfg|SET_APIC_DEST_FIELD(i)); /* Target chip */
+ cfg=apic_read(APIC_ICR);
+ cfg&=~0xCDFFF ; /* Clear bits */
+ cfg|=APIC_DEST_FIELD|APIC_DEST_DM_STARTUP|(((unsigned long)stack)>>12); /* Boot on the stack */
+ apic_write(APIC_ICR, cfg); /* Kick the second */
+
+ timeout = 0;
+ do {
+ udelay(1000);
+ if ((send_status = !(apic_read(APIC_ICR) & 0x00001000)))
+ break;
+ } while (timeout++ < 1000);
+
+ if (send_status) {
+ udelay(1000000);
+ apic_write(APIC_ESR, 0);
+ accept_status = (apic_read(APIC_ESR) & 0xEF);
+ }
+ }
+
+ if (!send_status) /* APIC never delivered?? */
+ printk("APIC never delivered???\n");
+ else if (accept_status) /* Send accept error */
+ printk("APIC delivery error (%lx).\n", accept_status);
+ else
+ {
+ for(timeout=0;timeout<50000;timeout++)
+ {
+ if(cpu_callin_map[0]&(1<<i))
+ break; /* It has booted */
+ udelay(100); /* Wait 5s total for a response */
+ }
+ if(cpu_callin_map[0]&(1<<i))
+ {
+ cpucount++;
+ /* number CPUs logically, starting from 1 (BSP is 0) */
+ cpu_number_map[i] = cpucount;
+ }
else
- printk("Not responding.\n");
- cpu_present_map&=~(1<<i);
+ {
+ if(*((volatile unsigned char *)8192)==0xA5)
+ printk("Stuck ??\n");
+ else
+ printk("Not responding val=(%lx).\n", *((unsigned long *) stack));
+ cpu_present_map&=~(1<<i);
+ cpu_number_map[i] = -1;
+ }
}
+
+ /* mark "stuck" area as not stuck */
+ *((volatile unsigned long *)8192) = 0;
}
+ else if (i == boot_cpu_id)
+ {
+ cpu_number_map[i] = 0;
+ }
+ else
+ {
+ cpu_number_map[i] = -1;
+ }
+
}
/*
* Allow the user to impress friends.
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov
with Sam's (original) version of this