True.
 
> Why don't you just move that into the top of smp_boot_cpus()?
Fixed - new patch below.
Martin.
--------------------------------------------------
diff -urN virgin-2.4.10/Documentation/Configure.help numa-2.4.10/Documentation/Configure.help
--- virgin-2.4.10/Documentation/Configure.help	Sun Sep 23 09:52:38 2001
+++ numa-2.4.10/Documentation/Configure.help	Wed Sep 26 16:04:34 2001
@@ -119,6 +119,14 @@
   SMP-FAQ on the WWW at http://www.irisa.fr/prive/mentre/smp-faq/ .
   
   If you don't know what to do here, say N.
+
+Multiquad support for NUMA systems
+CONFIG_MULTIQUAD
+  This option is used for getting Linux to run on a (IBM/Sequent) NUMA 
+  multiquad box. This changes the way that processors are bootstrapped,
+  and uses Clustered Logical APIC addressing mode instead of Flat Logical.
+  You will need a new lynxer.elf file to flash your firmware with - send
+  email to Martin.Bligh@us.ibm.com
   
 APIC and IO-APIC Support on Uniprocessors
 CONFIG_X86_UP_IOAPIC
diff -urN virgin-2.4.10/arch/i386/boot/compressed/misc.c numa-2.4.10/arch/i386/boot/compressed/misc.c
--- virgin-2.4.10/arch/i386/boot/compressed/misc.c	Tue Sep 11 23:02:02 2001
+++ numa-2.4.10/arch/i386/boot/compressed/misc.c	Wed Sep 26 16:04:34 2001
@@ -123,6 +123,10 @@
 static int vidport;
 static int lines, cols;
 
+#ifdef CONFIG_MULTIQUAD
+static void *xquad_portio = NULL;
+#endif
+
 #include "../../../../lib/inflate.c"
 
 static void *malloc(int size)
diff -urN virgin-2.4.10/arch/i386/config.in numa-2.4.10/arch/i386/config.in
--- virgin-2.4.10/arch/i386/config.in	Mon Sep 17 22:52:35 2001
+++ numa-2.4.10/arch/i386/config.in	Wed Sep 26 16:04:34 2001
@@ -175,6 +175,8 @@
       define_bool CONFIG_X86_IO_APIC y
       define_bool CONFIG_X86_LOCAL_APIC y
    fi
+else
+   bool 'Multiquad NUMA system' CONFIG_MULTIQUAD
 fi
 
 if [ "$CONFIG_SMP" = "y" -a "$CONFIG_X86_CMPXCHG" = "y" ]; then
diff -urN virgin-2.4.10/arch/i386/kernel/apic.c numa-2.4.10/arch/i386/kernel/apic.c
--- virgin-2.4.10/arch/i386/kernel/apic.c	Mon Sep 17 23:03:09 2001
+++ numa-2.4.10/arch/i386/kernel/apic.c	Wed Sep 26 16:24:26 2001
@@ -48,7 +48,7 @@
 	return maxlvt;
 }
 
-static void clear_local_APIC(void)
+void clear_local_APIC(void)
 {
 	int maxlvt;
 	unsigned long v;
@@ -254,6 +254,14 @@
 {
 	unsigned long value, ver, maxlvt;
 
+	/* Pound the ESR really hard over the head with a big hammer - mbligh */
+	if (esr_disable) {
+		apic_write(APIC_ESR, 0);
+		apic_write(APIC_ESR, 0);
+		apic_write(APIC_ESR, 0);
+		apic_write(APIC_ESR, 0);
+	}
+
 	value = apic_read(APIC_LVR);
 	ver = GET_APIC_VERSION(value);
 
@@ -262,8 +270,10 @@
 
 	/*
 	 * Double-check wether this APIC is really registered.
+	 * This is meaningless in clustered apic mode, so we skip it.
 	 */
-	if (!test_bit(GET_APIC_ID(apic_read(APIC_ID)), &phys_cpu_present_map))
+	if (!clustered_apic_mode && 
+	    !test_bit(GET_APIC_ID(apic_read(APIC_ID)), &phys_cpu_present_map))
 		BUG();
 
 	/*
@@ -272,19 +282,22 @@
 	 * document number 292116).  So here it goes...
 	 */
 
-	/*
-	 * Put the APIC into flat delivery mode.
-	 * Must be "all ones" explicitly for 82489DX.
-	 */
-	apic_write_around(APIC_DFR, 0xffffffff);
+	if (!clustered_apic_mode) {
+		/*
+		 * In clustered apic mode, the firmware does this for us 
+		 * Put the APIC into flat delivery mode.
+		 * Must be "all ones" explicitly for 82489DX.
+		 */
+		apic_write_around(APIC_DFR, 0xffffffff);
 
-	/*
-	 * Set up the logical destination ID.
-	 */
-	value = apic_read(APIC_LDR);
-	value &= ~APIC_LDR_MASK;
-	value |= (1<<(smp_processor_id()+24));
-	apic_write_around(APIC_LDR, value);
+		/*
+		 * Set up the logical destination ID.
+		 */
+		value = apic_read(APIC_LDR);
+		value &= ~APIC_LDR_MASK;
+		value |= (1<<(smp_processor_id()+24));
+		apic_write_around(APIC_LDR, value);
+	}
 
 	/*
 	 * Set Task Priority to 'accept all'. We never change this
@@ -367,7 +380,7 @@
 		value |= APIC_LVT_LEVEL_TRIGGER;
 	apic_write_around(APIC_LVT1, value);
 
-	if (APIC_INTEGRATED(ver)) {		/* !82489DX */
+	if (APIC_INTEGRATED(ver) && !esr_disable) {		/* !82489DX */
 		maxlvt = get_maxlvt();
 		if (maxlvt > 3)		/* Due to the Pentium erratum 3AP. */
 			apic_write(APIC_ESR, 0);
@@ -383,8 +396,18 @@
 			apic_write(APIC_ESR, 0);
 		value = apic_read(APIC_ESR);
 		printk("ESR value after enabling vector: %08lx\n", value);
-	} else
-		printk("No ESR for 82489DX.\n");
+	} else {
+		if (esr_disable)	
+			/* 
+			 * Something untraceble is creating bad interrupts on 
+			 * secondary quads ... for the moment, just leave the
+			 * ESR disabled - we can't do anything useful with the
+			 * errors anyway - mbligh
+			 */
+			printk("Leaving ESR disabled.\n");
+		else 
+			printk("No ESR for 82489DX.\n");
+	}
 
 	if (nmi_watchdog == NMI_LOCAL_APIC)
 		setup_apic_nmi_watchdog();
@@ -598,7 +621,7 @@
 	}
 	set_bit(X86_FEATURE_APIC, &boot_cpu_data.x86_capability);
 	mp_lapic_addr = APIC_DEFAULT_PHYS_BASE;
-	boot_cpu_id = 0;
+	boot_cpu_physical_apicid = 0;
 	if (nmi_watchdog != NMI_NONE)
 		nmi_watchdog = NMI_LOCAL_APIC;
 
@@ -636,8 +659,8 @@
 	 * Fetch the APIC ID of the BSP in case we have a
 	 * default configuration (or the MP table is broken).
 	 */
-	if (boot_cpu_id == -1U)
-		boot_cpu_id = GET_APIC_ID(apic_read(APIC_ID));
+	if (boot_cpu_physical_apicid == -1U)
+		boot_cpu_physical_apicid = GET_APIC_ID(apic_read(APIC_ID));
 
 #ifdef CONFIG_X86_IO_APIC
 	{
@@ -1077,9 +1100,9 @@
 	/*
 	 * Complain if the BIOS pretends there is one.
 	 */
-	if (!cpu_has_apic && APIC_INTEGRATED(apic_version[boot_cpu_id])) {
+	if (!cpu_has_apic && APIC_INTEGRATED(apic_version[boot_cpu_physical_apicid])) {
 		printk(KERN_ERR "BIOS bug, local APIC #%d not detected!...\n",
-			boot_cpu_id);
+			boot_cpu_physical_apicid);
 		return -1;
 	}
 
@@ -1088,7 +1111,7 @@
 	connect_bsp_APIC();
 
 	phys_cpu_present_map = 1;
-	apic_write_around(APIC_ID, boot_cpu_id);
+	apic_write_around(APIC_ID, boot_cpu_physical_apicid);
 
 	apic_pm_init2();
 
diff -urN virgin-2.4.10/arch/i386/kernel/io_apic.c numa-2.4.10/arch/i386/kernel/io_apic.c
--- virgin-2.4.10/arch/i386/kernel/io_apic.c	Mon Sep 17 23:03:09 2001
+++ numa-2.4.10/arch/i386/kernel/io_apic.c	Wed Sep 26 16:04:34 2001
@@ -44,11 +44,6 @@
  */
 int nr_ioapic_registers[MAX_IO_APICS];
 
-#if CONFIG_SMP
-# define TARGET_CPUS cpu_online_map
-#else
-# define TARGET_CPUS 0x01
-#endif
 /*
  * Rough estimation of how many shared IRQs there are, can
  * be changed anytime.
@@ -603,7 +598,7 @@
 		memset(&entry,0,sizeof(entry));
 
 		entry.delivery_mode = dest_LowestPrio;
-		entry.dest_mode = 1;			/* logical delivery */
+		entry.dest_mode = INT_DELIVERY_MODE;
 		entry.mask = 0;				/* enable IRQ */
 		entry.dest.logical.logical_dest = TARGET_CPUS;
 
@@ -677,7 +672,7 @@
 	 * We use logical delivery to get the timer IRQ
 	 * to the first CPU.
 	 */
-	entry.dest_mode = 1;				/* logical delivery */
+	entry.dest_mode = INT_DELIVERY_MODE;
 	entry.mask = 0;					/* unmask IRQ now */
 	entry.dest.logical.logical_dest = TARGET_CPUS;
 	entry.delivery_mode = dest_LowestPrio;
@@ -1016,6 +1011,9 @@
 	unsigned char old_id;
 	unsigned long flags;
 
+	if (clustered_apic_mode)
+		/* We don't have a good way to do this yet - hack */
+		phys_id_present_map = (u_long) 0xf;
 	/*
 	 * Set the IOAPIC ID to the value stored in the MPC table.
 	 */
@@ -1053,7 +1051,11 @@
 				i);
 			phys_id_present_map |= 1 << i;
 			mp_ioapics[apic].mpc_apicid = i;
+		} else {
+			printk("Setting %d in the phys_id_present_map\n", mp_ioapics[apic].mpc_apicid);
+			phys_id_present_map |= 1 << mp_ioapics[apic].mpc_apicid;
 		}
+
 
 		/*
 		 * We need to adjust the IRQ routing table
diff -urN virgin-2.4.10/arch/i386/kernel/mpparse.c numa-2.4.10/arch/i386/kernel/mpparse.c
--- virgin-2.4.10/arch/i386/kernel/mpparse.c	Mon Sep 17 23:03:09 2001
+++ numa-2.4.10/arch/i386/kernel/mpparse.c	Wed Sep 26 16:04:34 2001
@@ -54,7 +54,7 @@
 unsigned long mp_lapic_addr;
 
 /* Processor that is doing the boot up */
-unsigned int boot_cpu_id = -1U;
+unsigned int boot_cpu_physical_apicid = -1U;
 /* Internal processor count */
 static unsigned int num_processors;
 
@@ -180,8 +180,9 @@
 
 	if (m->mpc_cpuflag & CPU_BOOTPROCESSOR) {
 		Dprintk("    Bootup CPU\n");
-		boot_cpu_id = m->mpc_apicid;
+		boot_cpu_physical_apicid = m->mpc_apicid;
 	}
+
 	num_processors++;
 
 	if (m->mpc_apicid > MAX_APICS) {
@@ -191,7 +192,12 @@
 	}
 	ver = m->mpc_apicver;
 
-	phys_cpu_present_map |= 1 << m->mpc_apicid;
+	if (clustered_apic_mode)
+		/* Crude temporary hack. Assumes processors are sequential */
+		phys_cpu_present_map |= 1 << (num_processors-1);
+	else
+		phys_cpu_present_map |= 1 << m->mpc_apicid;
+
 	/*
 	 * Validate version
 	 */
@@ -376,6 +382,10 @@
 				break;
 			}
 		}
+	}
+	if (clustered_apic_mode && nr_ioapics > 2) {
+		/* don't initialise IO apics on secondary quads */
+		nr_ioapics = 2;
 	}
 	if (!num_processors)
 		printk(KERN_ERR "SMP mptable: no processors registered!\n");
diff -urN virgin-2.4.10/arch/i386/kernel/process.c numa-2.4.10/arch/i386/kernel/process.c
--- virgin-2.4.10/arch/i386/kernel/process.c	Mon Sep 17 22:52:35 2001
+++ numa-2.4.10/arch/i386/kernel/process.c	Wed Sep 26 16:38:53 2001
@@ -376,7 +376,7 @@
 		if ((reboot_cpu == -1) ||  
 		      (reboot_cpu > (NR_CPUS -1))  || 
 		      !(phys_cpu_present_map & (1<<cpuid))) 
-			reboot_cpu = boot_cpu_id;
+			reboot_cpu = boot_cpu_physical_apicid;
 
 		reboot_smp = 0;  /* use this as a flag to only go through this once*/
 		/* re-run this function on the other CPUs
diff -urN virgin-2.4.10/arch/i386/kernel/setup.c numa-2.4.10/arch/i386/kernel/setup.c
--- virgin-2.4.10/arch/i386/kernel/setup.c	Mon Sep 17 23:03:09 2001
+++ numa-2.4.10/arch/i386/kernel/setup.c	Wed Sep 26 16:04:34 2001
@@ -2416,7 +2416,12 @@
 	struct cpuinfo_x86 *c = cpu_data;
 	int i, n;
 
-	for (n = 0; n < NR_CPUS; n++, c++) {
+	/* 
+	 * WARNING - nasty evil hack ... if we print > 8, it overflows the
+	 * page buffer and corrupts memory - this needs fixing properly
+	 */
+	for (n = 0; n < 8; n++, c++) {
+	/* for (n = 0; n < NR_CPUS; n++, c++) { */
 		int fpu_exception;
 #ifdef CONFIG_SMP
 		if (!(cpu_online_map & (1<<n)))
@@ -2479,7 +2484,7 @@
 	return p - buffer;
 }
 
-static unsigned long cpu_initialized __initdata = 0;
+unsigned long cpu_initialized __initdata = 0;
 
 /*
  * cpu_init() initializes state that is per-CPU. Some data is already
diff -urN virgin-2.4.10/arch/i386/kernel/smp.c numa-2.4.10/arch/i386/kernel/smp.c
--- virgin-2.4.10/arch/i386/kernel/smp.c	Mon Sep 17 23:03:09 2001
+++ numa-2.4.10/arch/i386/kernel/smp.c	Wed Sep 26 16:04:34 2001
@@ -20,6 +20,7 @@
 
 #include <asm/mtrr.h>
 #include <asm/pgalloc.h>
+#include <asm/smpboot.h>
 
 /*
  *	Some notes on x86 processor bugs affecting SMP operation:
@@ -148,28 +149,12 @@
 	apic_write_around(APIC_ICR, cfg);
 }
 
-static inline void send_IPI_allbutself(int vector)
-{
-	/*
-	 * if there are no other CPUs in the system then
-	 * we get an APIC send error if we try to broadcast.
-	 * thus we have to avoid sending IPIs in this case.
-	 */
-	if (smp_num_cpus > 1)
-		__send_IPI_shortcut(APIC_DEST_ALLBUT, vector);
-}
-
-static inline void send_IPI_all(int vector)
-{
-	__send_IPI_shortcut(APIC_DEST_ALLINC, vector);
-}
-
 void send_IPI_self(int vector)
 {
 	__send_IPI_shortcut(APIC_DEST_SELF, vector);
 }
 
-static inline void send_IPI_mask(int mask, int vector)
+static inline void send_IPI_mask_bitmask(int mask, int vector)
 {
 	unsigned long cfg;
 	unsigned long flags;
@@ -177,27 +162,120 @@
 	__save_flags(flags);
 	__cli();
 
+		
 	/*
 	 * Wait for idle.
 	 */
 	apic_wait_icr_idle();
-
+		
 	/*
 	 * prepare target chip field
 	 */
 	cfg = __prepare_ICR2(mask);
 	apic_write_around(APIC_ICR2, cfg);
-
+		
 	/*
 	 * program the ICR 
 	 */
 	cfg = __prepare_ICR(0, vector);
-	
+			
 	/*
 	 * Send the IPI. The write to APIC_ICR fires this off.
 	 */
 	apic_write_around(APIC_ICR, cfg);
+
 	__restore_flags(flags);
+}
+
+static inline void send_IPI_mask_sequence(int mask, int vector)
+{
+	unsigned long cfg, flags;
+	unsigned int query_cpu, query_mask;
+
+	/*
+	 * Hack. The clustered APIC addressing mode doesn't allow us to send 
+	 * to an arbitrary mask, so I do a unicasts to each CPU instead. This 
+	 * should be modified to do 1 message per cluster ID - mbligh
+	 */ 
+
+	__save_flags(flags);
+	__cli();
+
+	for (query_cpu = 0; query_cpu < NR_CPUS; ++query_cpu) {
+		query_mask = 1 << query_cpu;
+		if (query_mask & mask) {
+		
+			/*
+			 * Wait for idle.
+			 */
+			apic_wait_icr_idle();
+		
+			/*
+			 * prepare target chip field
+			 */
+			cfg = __prepare_ICR2(cpu_to_logical_apicid(query_cpu));
+			apic_write_around(APIC_ICR2, cfg);
+		
+			/*
+			 * program the ICR 
+			 */
+			cfg = __prepare_ICR(0, vector);
+			
+			/*
+			 * Send the IPI. The write to APIC_ICR fires this off.
+			 */
+			apic_write_around(APIC_ICR, cfg);
+		}
+	}
+	__restore_flags(flags);
+}
+
+static inline void send_IPI_mask(int mask, int vector)
+{
+	if (clustered_apic_mode) 
+		send_IPI_mask_sequence(mask, vector);
+	else
+		send_IPI_mask_bitmask(mask, vector);
+}
+
+static inline void send_IPI_allbutself(int vector)
+{
+	/*
+	 * if there are no other CPUs in the system then
+	 * we get an APIC send error if we try to broadcast.
+	 * thus we have to avoid sending IPIs in this case.
+	 */
+	if (!(smp_num_cpus > 1))
+		return;
+
+	if (clustered_apic_mode) {
+		// Pointless. Use send_IPI_mask to do this instead
+		int cpu;
+
+		if (smp_num_cpus > 1) {
+			for (cpu = 0; cpu < smp_num_cpus; ++cpu) {
+				if (cpu != smp_processor_id())
+					send_IPI_mask(1 << cpu, vector);
+			}
+		}
+	} else {
+		__send_IPI_shortcut(APIC_DEST_ALLBUT, vector);
+		return;
+	}
+}
+
+static inline void send_IPI_all(int vector)
+{
+	if (clustered_apic_mode) {
+		// Pointless. Use send_IPI_mask to do this instead
+		int cpu;
+
+		for (cpu = 0; cpu < smp_num_cpus; ++cpu) {
+			send_IPI_mask(1 << cpu, vector);
+		}
+	} else {
+		__send_IPI_shortcut(APIC_DEST_ALLINC, vector);
+	}
 }
 
 /*
diff -urN virgin-2.4.10/arch/i386/kernel/smpboot.c numa-2.4.10/arch/i386/kernel/smpboot.c
--- virgin-2.4.10/arch/i386/kernel/smpboot.c	Mon Sep 17 23:03:09 2001
+++ numa-2.4.10/arch/i386/kernel/smpboot.c	Fri Sep 28 15:23:32 2001
@@ -29,6 +29,7 @@
  *		Ingo Molnar	:	various cleanups and rewrites
  *		Tigran Aivazian	:	fixed "0.00 in /proc/uptime on SMP" bug.
  *	Maciej W. Rozycki	:	Bits for genuine 82489DX APICs
+ *		Martin J. Bligh	: 	Added support for multi-quad systems
  */
 
 #include <linux/config.h>
@@ -44,6 +45,7 @@
 #include <linux/mc146818rtc.h>
 #include <asm/mtrr.h>
 #include <asm/pgalloc.h>
+#include <asm/smpboot.h>
 
 /* Set if we find a B stepping CPU			*/
 static int smp_b_stepping;
@@ -57,11 +59,6 @@
 /* Bitmask of currently online CPUs */
 unsigned long cpu_online_map;
 
-/* which CPU (physical APIC ID) maps to which logical CPU number */
-volatile int x86_apicid_to_cpu[NR_CPUS];
-/* which logical CPU number maps to which CPU (physical APIC ID) */
-volatile int x86_cpu_to_apicid[NR_CPUS];
-
 static volatile unsigned long cpu_callin_map;
 static volatile unsigned long cpu_callout_map;
 
@@ -357,7 +354,8 @@
 	 * our local APIC.  We have to wait for the IPI or we'll
 	 * lock up on an APIC access.
 	 */
-	while (!atomic_read(&init_deasserted));
+	if (!clustered_apic_mode) 
+		while (!atomic_read(&init_deasserted));
 
 	/*
 	 * (This works even if the APIC is not enabled.)
@@ -406,9 +404,15 @@
 	 */
 
 	Dprintk("CALLIN, before setup_local_APIC().\n");
+	/*
+	 * Because we use NMIs rather than the INIT-STARTUP sequence to
+	 * bootstrap the CPUs, the APIC may be in a wierd state. Kick it.
+	 */
+	if (clustered_apic_mode)
+		clear_local_APIC();
 	setup_local_APIC();
 
-	sti();
+	__sti();
 
 #ifdef CONFIG_MTRR
 	/*
@@ -458,11 +462,20 @@
 	while (!atomic_read(&smp_commenced))
 		rep_nop();
 	/*
+	 * Removing the next 2 printk's causes an undiagnosable BINT error
+	 * in multiquad mode. Seems the console lock serialises the procs? 
+	 * As of 2.4.10, these printk's panic the kernel if I have serial
+	 * console enabled. Aarrrgggh. mbligh
+	 */
+	if (clustered_apic_mode)
+		printk("Before tlbflush - processor: %d\n", current->processor);
+	/*
 	 * low-memory mappings have been cleared, flush them from
 	 * the local TLBs too.
 	 */
 	local_flush_tlb();
-
+	if (clustered_apic_mode)
+		printk("After tlbflush - processor: %d\n", current->processor);
 	return cpu_idle();
 }
 
@@ -501,6 +514,61 @@
 	return do_fork(CLONE_VM|CLONE_PID, 0, ®s, 0);
 }
 
+/* which physical APIC ID maps to which logical CPU number */
+volatile int physical_apicid_2_cpu[MAX_APICID];
+/* which logical CPU number maps to which physical APIC ID */
+volatile int cpu_2_physical_apicid[NR_CPUS];
+
+/* which logical APIC ID maps to which logical CPU number */
+volatile int logical_apicid_2_cpu[MAX_APICID];
+/* which logical CPU number maps to which logical APIC ID */
+volatile int cpu_2_logical_apicid[NR_CPUS];
+
+static inline void init_cpu_to_apicid(void)
+/* Initialize all maps between cpu number and apicids */
+{
+	int apicid, cpu;
+
+	for (apicid = 0; apicid < MAX_APICID; apicid++) {
+		physical_apicid_2_cpu[apicid] = -1;
+		logical_apicid_2_cpu[apicid] = -1;
+	}
+	for (cpu = 0; cpu < NR_CPUS; cpu++) {
+		cpu_2_physical_apicid[cpu] = -1;
+		cpu_2_logical_apicid[cpu] = -1;
+	}
+}
+
+static inline void map_cpu_to_boot_apicid(int cpu, int apicid)
+/* 
+ * set up a mapping between cpu and apicid. Uses logical apicids for multiquad,
+ * else physical apic ids
+ */
+{
+	if (clustered_apic_mode) {
+		logical_apicid_2_cpu[apicid] = cpu;	
+		cpu_2_logical_apicid[cpu] = apicid;
+	} else {
+		physical_apicid_2_cpu[apicid] = cpu;	
+		cpu_2_physical_apicid[cpu] = apicid;
+	}
+}
+
+static inline void unmap_cpu_to_boot_apicid(int cpu, int apicid)
+/* 
+ * undo a mapping between cpu and apicid. Uses logical apicids for multiquad,
+ * else physical apic ids
+ */
+{
+	if (clustered_apic_mode) {
+		logical_apicid_2_cpu[apicid] = -1;	
+		cpu_2_logical_apicid[cpu] = -1;
+	} else {
+		physical_apicid_2_cpu[apicid] = -1;	
+		cpu_2_physical_apicid[cpu] = -1;
+	}
+}
+
 #if APIC_DEBUG
 static inline void inquire_remote_apic(int apicid)
 {
@@ -539,89 +607,65 @@
 }
 #endif
 
-static void __init do_boot_cpu (int apicid)
+static int wakeup_secondary_via_NMI(int logical_apicid)
+/* 
+ * Poke the other CPU in the eye to wake it up. Remember that the normal
+ * INIT, INIT, STARTUP sequence will reset the chip hard for us, and this
+ * won't ... remember to clear down the APIC, etc later.
+ */
 {
-	struct task_struct *idle;
-	unsigned long send_status, accept_status, boot_status, maxlvt;
-	int timeout, num_starts, j, cpu;
-	unsigned long start_eip;
-
-	cpu = ++cpucount;
-	/*
-	 * We can't use kernel_thread since we must avoid to
-	 * reschedule the child.
-	 */
-	if (fork_by_hand() < 0)
-		panic("failed fork for CPU %d", cpu);
-
-	/*
-	 * We remove it from the pidhash and the runqueue
-	 * once we got the process:
-	 */
-	idle = init_task.prev_task;
-	if (!idle)
-		panic("No idle process for CPU %d", cpu);
+	unsigned long send_status = 0, accept_status = 0;
+	int timeout, maxlvt;
 
-	idle->processor = cpu;
-	x86_cpu_to_apicid[cpu] = apicid;
-	x86_apicid_to_cpu[apicid] = cpu;
-	idle->has_cpu = 1; /* we schedule the first task manually */
-	idle->thread.eip = (unsigned long) start_secondary;
-
-	del_from_runqueue(idle);
-	unhash_process(idle);
-	init_tasks[cpu] = idle;
+	/* Target chip */
+	apic_write_around(APIC_ICR2, SET_APIC_DEST_FIELD(logical_apicid));
 
-	/* start_eip had better be page-aligned! */
-	start_eip = setup_trampoline();
+	/* Boot on the stack */
+	/* Kick the second */
+	apic_write_around(APIC_ICR, APIC_DM_NMI | APIC_DEST_LOGICAL);
 
-	/* So we see what's up   */
-	printk("Booting processor %d/%d eip %lx\n", cpu, apicid, start_eip);
-	stack_start.esp = (void *) (1024 + PAGE_SIZE + (char *)idle);
+	Dprintk("Waiting for send to finish...\n");
+	timeout = 0;
+	do {
+		Dprintk("+");
+		udelay(100);
+		send_status = apic_read(APIC_ICR) & APIC_ICR_BUSY;
+	} while (send_status && (timeout++ < 1000));
 
 	/*
-	 * This grunge runs the startup process for
-	 * the targeted processor.
+	 * Give the other CPU some time to accept the IPI.
 	 */
-
-	atomic_set(&init_deasserted, 0);
-
-	Dprintk("Setting warm reset code and vector.\n");
-
-	CMOS_WRITE(0xa, 0xf);
-	local_flush_tlb();
-	Dprintk("1.\n");
-	*((volatile unsigned short *) phys_to_virt(0x469)) = start_eip >> 4;
-	Dprintk("2.\n");
-	*((volatile unsigned short *) phys_to_virt(0x467)) = start_eip & 0xf;
-	Dprintk("3.\n");
-
+	udelay(200);
 	/*
-	 * Be paranoid about clearing APIC errors.
+	 * Due to the Pentium erratum 3AP.
 	 */
-	if (APIC_INTEGRATED(apic_version[apicid])) {
+	maxlvt = get_maxlvt();
+	if (maxlvt > 3) {
 		apic_read_around(APIC_SPIV);
 		apic_write(APIC_ESR, 0);
-		apic_read(APIC_ESR);
 	}
+	accept_status = (apic_read(APIC_ESR) & 0xEF);
+	Dprintk("NMI sent.\n");
 
-	/*
-	 * Status is now clean
-	 */
-	send_status = 0;
-	accept_status = 0;
-	boot_status = 0;
+	if (send_status)
+		printk("APIC never delivered???\n");
+	if (accept_status)
+		printk("APIC delivery error (%lx).\n", accept_status);
 
-	/*
-	 * Starting actual IPI sequence...
-	 */
+	return (send_status | accept_status);
+}
+
+static int wakeup_secondary_via_INIT(int phys_apicid, unsigned long start_eip)
+{
+	unsigned long send_status = 0, accept_status = 0;
+	int maxlvt, timeout, num_starts, j;
 
 	Dprintk("Asserting INIT.\n");
 
 	/*
 	 * Turn INIT on target chip
 	 */
-	apic_write_around(APIC_ICR2, SET_APIC_DEST_FIELD(apicid));
+	apic_write_around(APIC_ICR2, SET_APIC_DEST_FIELD(phys_apicid));
 
 	/*
 	 * Send IPI
@@ -642,7 +686,7 @@
 	Dprintk("Deasserting INIT.\n");
 
 	/* Target chip */
-	apic_write_around(APIC_ICR2, SET_APIC_DEST_FIELD(apicid));
+	apic_write_around(APIC_ICR2, SET_APIC_DEST_FIELD(phys_apicid));
 
 	/* Send IPI */
 	apic_write_around(APIC_ICR, APIC_INT_LEVELTRIG | APIC_DM_INIT);
@@ -661,10 +705,9 @@
 	 * Should we send STARTUP IPIs ?
 	 *
 	 * Determine this based on the APIC version.
-	 * If we don't have an integrated APIC, don't
-	 * send the STARTUP IPIs.
+	 * If we don't have an integrated APIC, don't send the STARTUP IPIs.
 	 */
-	if (APIC_INTEGRATED(apic_version[apicid]))
+	if (APIC_INTEGRATED(apic_version[phys_apicid]))
 		num_starts = 2;
 	else
 		num_starts = 0;
@@ -688,7 +731,7 @@
 		 */
 
 		/* Target chip */
-		apic_write_around(APIC_ICR2, SET_APIC_DEST_FIELD(apicid));
+		apic_write_around(APIC_ICR2, SET_APIC_DEST_FIELD(phys_apicid));
 
 		/* Boot on the stack */
 		/* Kick the second */
@@ -732,7 +775,104 @@
 	if (accept_status)
 		printk("APIC delivery error (%lx).\n", accept_status);
 
-	if (!send_status && !accept_status) {
+	return (send_status | accept_status);
+}
+
+extern unsigned long cpu_initialized;
+
+static void __init do_boot_cpu (int apicid) 
+/*
+ * NOTE - on most systems this is a PHYSICAL apic ID, but on multiquad
+ * (ie clustered apic addressing mode), this is a LOGICAL apic ID.
+ */
+{
+	struct task_struct *idle;
+	unsigned long boot_error = 0;
+	int timeout, cpu;
+	unsigned long start_eip;
+	unsigned short nmi_high, nmi_low;
+
+	cpu = ++cpucount;
+	/*
+	 * We can't use kernel_thread since we must avoid to
+	 * reschedule the child.
+	 */
+	if (fork_by_hand() < 0)
+		panic("failed fork for CPU %d", cpu);
+
+	/*
+	 * We remove it from the pidhash and the runqueue
+	 * once we got the process:
+	 */
+	idle = init_task.prev_task;
+	if (!idle)
+		panic("No idle process for CPU %d", cpu);
+
+	idle->processor = cpu;
+
+	map_cpu_to_boot_apicid(cpu, apicid);
+
+	idle->has_cpu = 1; /* we schedule the first task manually */
+	idle->thread.eip = (unsigned long) start_secondary;
+
+	del_from_runqueue(idle);
+	unhash_process(idle);
+	init_tasks[cpu] = idle;
+
+	/* start_eip had better be page-aligned! */
+	start_eip = setup_trampoline();
+
+	/* So we see what's up   */
+	printk("Booting processor %d/%d eip %lx\n", cpu, apicid, start_eip);
+	stack_start.esp = (void *) (1024 + PAGE_SIZE + (char *)idle);
+
+	/*
+	 * This grunge runs the startup process for
+	 * the targeted processor.
+	 */
+
+	atomic_set(&init_deasserted, 0);
+
+	Dprintk("Setting warm reset code and vector.\n");
+
+	if (clustered_apic_mode) {
+		/* stash the current NMI vector, so we can put things back */
+		nmi_high = *((volatile unsigned short *) TRAMPOLINE_HIGH);
+		nmi_low = *((volatile unsigned short *) TRAMPOLINE_LOW);
+	} 
+
+	CMOS_WRITE(0xa, 0xf);
+	local_flush_tlb();
+	Dprintk("1.\n");
+	*((volatile unsigned short *) TRAMPOLINE_HIGH) = start_eip >> 4;
+	Dprintk("2.\n");
+	*((volatile unsigned short *) TRAMPOLINE_LOW) = start_eip & 0xf;
+	Dprintk("3.\n");
+
+	/*
+	 * Be paranoid about clearing APIC errors.
+	 */
+	if (!clustered_apic_mode && APIC_INTEGRATED(apic_version[apicid])) {
+		apic_read_around(APIC_SPIV);
+		apic_write(APIC_ESR, 0);
+		apic_read(APIC_ESR);
+	}
+
+	/*
+	 * Status is now clean
+	 */
+	boot_error = 0;
+
+	/*
+	 * Starting actual IPI sequence...
+	 */
+
+	if (clustered_apic_mode)
+		boot_error = wakeup_secondary_via_NMI(apicid);
+	else 
+		boot_error = wakeup_secondary_via_INIT(apicid, start_eip);
+
+	if (!boot_error) {
 		/*
 		 * allow APs to start initializing.
 		 */
@@ -756,7 +896,7 @@
 			print_cpu_info(&cpu_data[cpu]);
 			Dprintk("CPU has booted.\n");
 		} else {
-			boot_status = 1;
+			boot_error= 1;
 			if (*((volatile unsigned char *)phys_to_virt(8192))
 					== 0xA5)
 				/* trampoline started but...? */
@@ -765,18 +905,28 @@
 				/* trampoline code not run */
 				printk("Not responding.\n");
 #if APIC_DEBUG
-			inquire_remote_apic(apicid);
+			if (!clustered_apic_mode)
+				inquire_remote_apic(apicid);
 #endif
 		}
 	}
-	if (send_status || accept_status || boot_status) {
-		x86_cpu_to_apicid[cpu] = -1;
-		x86_apicid_to_cpu[apicid] = -1;
+	if (boot_error) {
+		/* Try to put things back the way they were before ... */
+		unmap_cpu_to_boot_apicid(cpu, apicid);
+		clear_bit(cpu, &cpu_callout_map); /* was set here (do_boot_cpu()) */
+		clear_bit(cpu, &cpu_initialized); /* was set by cpu_init() */
+		clear_bit(cpu, &cpu_online_map);  /* was set in smp_callin() */
 		cpucount--;
 	}
 
 	/* mark "stuck" area as not stuck */
 	*((volatile unsigned long *)phys_to_virt(8192)) = 0;
+
+	if(clustered_apic_mode) {
+		printk("Restoring NMI vector\n");
+		*((volatile unsigned short *) TRAMPOLINE_HIGH) = nmi_high;
+		*((volatile unsigned short *) TRAMPOLINE_LOW) = nmi_low;
+	}
 }
 
 cycles_t cacheflush_time;
@@ -826,9 +976,20 @@
 extern int prof_old_multiplier[NR_CPUS];
 extern int prof_counter[NR_CPUS];
 
+static int boot_cpu_logical_apicid;
+/* Where the IO area was mapped on multiquad, always 0 otherwise */
+void *xquad_portio = NULL;
+
 void __init smp_boot_cpus(void)
 {
-	int apicid, cpu;
+	int apicid, cpu, bit;
+
+        if (clustered_apic_mode) {
+                /* remap the 1st quad's 256k range for cross-quad I/O */
+                xquad_portio = ioremap (XQUAD_PORTIO_BASE, XQUAD_PORTIO_LEN);
+                printk("Cross quad port I/O vaddr 0x%08lx, len %08lx\n",
+                        (u_long) xquad_portio, (u_long) XQUAD_PORTIO_LEN);
+        }
 
 #ifdef CONFIG_MTRR
 	/*  Must be done before other processors booted  */
@@ -839,13 +1000,14 @@
 	 * and the per-CPU profiling counter/multiplier
 	 */
 
-	for (apicid = 0; apicid < NR_CPUS; apicid++) {
-		x86_apicid_to_cpu[apicid] = -1;
-		prof_counter[apicid] = 1;
-		prof_old_multiplier[apicid] = 1;
-		prof_multiplier[apicid] = 1;
+	for (cpu = 0; cpu < NR_CPUS; cpu++) {
+		prof_counter[cpu] = 1;
+		prof_old_multiplier[cpu] = 1;
+		prof_multiplier[cpu] = 1;
 	}
 
+	init_cpu_to_apicid();
+
 	/*
 	 * Setup boot CPU information
 	 */
@@ -857,8 +1019,9 @@
 	 * We have the boot CPU online for sure.
 	 */
 	set_bit(0, &cpu_online_map);
-	x86_apicid_to_cpu[boot_cpu_id] = 0;
-	x86_cpu_to_apicid[0] = boot_cpu_id;
+	boot_cpu_logical_apicid = logical_smp_processor_id();
+	map_cpu_to_boot_apicid(0, boot_cpu_apicid);
+
 	global_irq_holder = 0;
 	current->processor = 0;
 	init_idle();
@@ -884,20 +1047,22 @@
 	/*
 	 * Should not be necessary because the MP table should list the boot
 	 * CPU too, but we do it for the sake of robustness anyway.
+	 * Makes no sense to do this check in clustered apic mode, so skip it
 	 */
-	if (!test_bit(boot_cpu_id, &phys_cpu_present_map)) {
+	if (!clustered_apic_mode && 
+	    !test_bit(boot_cpu_physical_apicid, &phys_cpu_present_map)) {
 		printk("weird, boot CPU (#%d) not listed by the BIOS.\n",
-								 boot_cpu_id);
+							boot_cpu_physical_apicid);
 		phys_cpu_present_map |= (1 << hard_smp_processor_id());
 	}
 
 	/*
 	 * If we couldn't find a local APIC, then get out of here now!
 	 */
-	if (APIC_INTEGRATED(apic_version[boot_cpu_id]) &&
+	if (APIC_INTEGRATED(apic_version[boot_cpu_physical_apicid]) &&
 	    !test_bit(X86_FEATURE_APIC, boot_cpu_data.x86_capability)) {
 		printk(KERN_ERR "BIOS bug, local APIC #%d not detected!...\n",
-			boot_cpu_id);
+			boot_cpu_physical_apicid);
 		printk(KERN_ERR "... forcing use of dummy APIC emulation. (tell your hw vendor)\n");
 #ifndef CONFIG_VISWS
 		io_apic_irqs = 0;
@@ -926,22 +1091,27 @@
 	connect_bsp_APIC();
 	setup_local_APIC();
 
-	if (GET_APIC_ID(apic_read(APIC_ID)) != boot_cpu_id)
+	if (GET_APIC_ID(apic_read(APIC_ID)) != boot_cpu_physical_apicid)
 		BUG();
 
 	/*
-	 * Now scan the CPU present map and fire up the other CPUs.
+	 * Scan the CPU present map and fire up the other CPUs via do_boot_cpu
+	 *
+	 * In clustered apic mode, phys_cpu_present_map is a constructed thus:
+	 * bits 0-3 are quad0, 4-7 are quad1, etc. A perverse twist on the 
+	 * clustered apic ID.
 	 */
 	Dprintk("CPU present map: %lx\n", phys_cpu_present_map);
 
-	for (apicid = 0; apicid < NR_CPUS; apicid++) {
+	for (bit = 0; bit < NR_CPUS; bit++) {
+		apicid = cpu_present_to_apicid(bit);
 		/*
 		 * Don't even attempt to start the boot CPU!
 		 */
-		if (apicid == boot_cpu_id)
+		if (apicid == boot_cpu_apicid)
 			continue;
 
-		if (!(phys_cpu_present_map & (1 << apicid)))
+		if (!(phys_cpu_present_map & (1 << bit)))
 			continue;
 		if ((max_cpus >= 0) && (max_cpus <= cpucount+1))
 			continue;
@@ -951,9 +1121,10 @@
 		/*
 		 * Make sure we unmap all failed CPUs
 		 */
-		if ((x86_apicid_to_cpu[apicid] == -1) &&
-				(phys_cpu_present_map & (1 << apicid)))
-			printk("phys CPU #%d not responding - cannot use it.\n",apicid);
+		if ((boot_apicid_to_cpu(apicid) == -1) &&
+				(phys_cpu_present_map & (1 << bit)))
+			printk("CPU #%d not responding - cannot use it.\n",
+								apicid);
 	}
 
 	/*
diff -urN virgin-2.4.10/arch/i386/kernel/trampoline.S numa-2.4.10/arch/i386/kernel/trampoline.S
--- virgin-2.4.10/arch/i386/kernel/trampoline.S	Mon Feb  7 19:59:39 2000
+++ numa-2.4.10/arch/i386/kernel/trampoline.S	Wed Sep 26 16:04:34 2001
@@ -36,7 +36,9 @@
 
 ENTRY(trampoline_data)
 r_base = .
-
+#ifdef CONFIG_MULTIQUAD
+	wbinvd
+#endif /* CONFIG_MULTIQUAD */
 	mov	%cs, %ax	# Code and data in the same place
 	mov	%ax, %ds
 
diff -urN virgin-2.4.10/include/asm-i386/apic.h numa-2.4.10/include/asm-i386/apic.h
--- virgin-2.4.10/include/asm-i386/apic.h	Sun Sep 23 10:31:02 2001
+++ numa-2.4.10/include/asm-i386/apic.h	Wed Sep 26 16:04:35 2001
@@ -64,6 +64,7 @@
 }
 
 extern int get_maxlvt(void);
+extern void clear_local_APIC(void);
 extern void connect_bsp_APIC (void);
 extern void disconnect_bsp_APIC (void);
 extern void disable_local_APIC (void);
diff -urN virgin-2.4.10/include/asm-i386/io.h numa-2.4.10/include/asm-i386/io.h
--- virgin-2.4.10/include/asm-i386/io.h	Sun Sep 23 10:31:22 2001
+++ numa-2.4.10/include/asm-i386/io.h	Wed Sep 26 16:04:35 2001
@@ -36,75 +36,11 @@
   *  - Arnaldo Carvalho de Melo <acme@conectiva.com.br>
   */
 
-#ifdef SLOW_IO_BY_JUMPING
-#define __SLOW_DOWN_IO "\njmp 1f\n1:\tjmp 1f\n1:"
-#else
-#define __SLOW_DOWN_IO "\noutb %%al,$0x80"
-#endif
-
-#ifdef REALLY_SLOW_IO
-#define __FULL_SLOW_DOWN_IO __SLOW_DOWN_IO __SLOW_DOWN_IO __SLOW_DOWN_IO __SLOW_DOWN_IO
-#else
-#define __FULL_SLOW_DOWN_IO __SLOW_DOWN_IO
-#endif
-
-/*
- * Talk about misusing macros..
- */
-#define __OUT1(s,x) \
-static inline void out##s(unsigned x value, unsigned short port) {
-
-#define __OUT2(s,s1,s2) \
-__asm__ __volatile__ ("out" #s " %" s1 "0,%" s2 "1"
-
-#define __OUT(s,s1,x) \
-__OUT1(s,x) __OUT2(s,s1,"w") : : "a" (value), "Nd" (port)); } \
-__OUT1(s##_p,x) __OUT2(s,s1,"w") __FULL_SLOW_DOWN_IO : : "a" (value), "Nd" (port));} \
-
-#define __IN1(s) \
-static inline RETURN_TYPE in##s(unsigned short port) { RETURN_TYPE _v;
-
-#define __IN2(s,s1,s2) \
-__asm__ __volatile__ ("in" #s " %" s2 "1,%" s1 "0"
-
-#define __IN(s,s1,i...) \
-__IN1(s) __IN2(s,s1,"w") : "=a" (_v) : "Nd" (port) ,##i ); return _v; } \
-__IN1(s##_p) __IN2(s,s1,"w") __FULL_SLOW_DOWN_IO : "=a" (_v) : "Nd" (port) ,##i ); return _v; } \
-
-#define __INS(s) \
-static inline void ins##s(unsigned short port, void * addr, unsigned long count) \
-{ __asm__ __volatile__ ("rep ; ins" #s \
-: "=D" (addr), "=c" (count) : "d" (port),"0" (addr),"1" (count)); }
-
-#define __OUTS(s) \
-static inline void outs##s(unsigned short port, const void * addr, unsigned long count) \
-{ __asm__ __volatile__ ("rep ; outs" #s \
-: "=S" (addr), "=c" (count) : "d" (port),"0" (addr),"1" (count)); }
-
-#define RETURN_TYPE unsigned char
-__IN(b,"")
-#undef RETURN_TYPE
-#define RETURN_TYPE unsigned short
-__IN(w,"")
-#undef RETURN_TYPE
-#define RETURN_TYPE unsigned int
-__IN(l,"")
-#undef RETURN_TYPE
-
-__OUT(b,"b",char)
-__OUT(w,"w",short)
-__OUT(l,,int)
-
-__INS(b)
-__INS(w)
-__INS(l)
-
-__OUTS(b)
-__OUTS(w)
-__OUTS(l)
-
 #define IO_SPACE_LIMIT 0xffff
 
+#define XQUAD_PORTIO_BASE 0xfe400000
+#define XQUAD_PORTIO_LEN  0x40000   /* 256k per quad. Only remapping 1st */
+
 #ifdef __KERNEL__
 
 #include <linux/vmalloc.h>
@@ -260,5 +196,111 @@
 #define dma_cache_wback_inv(_start,_size)	do { } while (0)
 
 #endif /* __KERNEL__ */
+
+#ifdef SLOW_IO_BY_JUMPING
+#define __SLOW_DOWN_IO "\njmp 1f\n1:\tjmp 1f\n1:"
+#else
+#define __SLOW_DOWN_IO "\noutb %%al,$0x80"
+#endif
+
+#ifdef REALLY_SLOW_IO
+#define __FULL_SLOW_DOWN_IO __SLOW_DOWN_IO __SLOW_DOWN_IO __SLOW_DOWN_IO __SLOW_DOWN_IO
+#else
+#define __FULL_SLOW_DOWN_IO __SLOW_DOWN_IO
+#endif
+
+#ifdef CONFIG_MULTIQUAD
+extern void *xquad_portio;    /* Where the IO area was mapped */
+#endif /* CONFIG_MULTIQUAD */
+
+/*
+ * Talk about misusing macros..
+ */
+#define __OUT1(s,x) \
+static inline void out##s(unsigned x value, unsigned short port) {
+
+#define __OUT2(s,s1,s2) \
+__asm__ __volatile__ ("out" #s " %" s1 "0,%" s2 "1"
+
+#ifdef CONFIG_MULTIQUAD
+/* Make the default portio routines operate on quad 0 for now */
+#define __OUT(s,s1,x) \
+__OUT1(s##_local,x) __OUT2(s,s1,"w") : : "a" (value), "Nd" (port)); } \
+__OUT1(s##_p_local,x) __OUT2(s,s1,"w") __FULL_SLOW_DOWN_IO : : "a" (value), "Nd" (port));} \
+__OUTQ0(s,s,x) \
+__OUTQ0(s,s##_p,x) 
+#else
+#define __OUT(s,s1,x) \
+__OUT1(s,x) __OUT2(s,s1,"w") : : "a" (value), "Nd" (port)); } \
+__OUT1(s##_p,x) __OUT2(s,s1,"w") __FULL_SLOW_DOWN_IO : : "a" (value), "Nd" (port));} 
+#endif /* CONFIG_MULTIQUAD */
+
+#ifdef CONFIG_MULTIQUAD
+#define __OUTQ0(s,ss,x)    /* Do the equivalent of the portio op on quad 0 */ \
+static inline void out##ss(unsigned x value, unsigned short port) { \
+	if (xquad_portio) \
+		write##s(value, (unsigned long) xquad_portio + port); \
+	else               /* We're still in early boot, running on quad 0 */ \
+		out##ss##_local(value, port); \
+} 
+
+#define __INQ0(s,ss)       /* Do the equivalent of the portio op on quad 0 */ \
+static inline RETURN_TYPE in##ss(unsigned short port) { \
+	if (xquad_portio) \
+		return read##s((unsigned long) xquad_portio + port); \
+	else               /* We're still in early boot, running on quad 0 */ \
+		return in##ss##_local(port); \
+}
+#endif /* CONFIG_MULTIQUAD */
+
+#define __IN1(s) \
+static inline RETURN_TYPE in##s(unsigned short port) { RETURN_TYPE _v;
+
+#define __IN2(s,s1,s2) \
+__asm__ __volatile__ ("in" #s " %" s2 "1,%" s1 "0"
+
+#ifdef CONFIG_MULTIQUAD
+#define __IN(s,s1,i...) \
+__IN1(s##_local) __IN2(s,s1,"w") : "=a" (_v) : "Nd" (port) ,##i ); return _v; } \
+__IN1(s##_p_local) __IN2(s,s1,"w") __FULL_SLOW_DOWN_IO : "=a" (_v) : "Nd" (port) ,##i ); return _v; } \
+__INQ0(s,s) \
+__INQ0(s,s##_p) 
+#else
+#define __IN(s,s1,i...) \
+__IN1(s) __IN2(s,s1,"w") : "=a" (_v) : "Nd" (port) ,##i ); return _v; } \
+__IN1(s##_p) __IN2(s,s1,"w") __FULL_SLOW_DOWN_IO : "=a" (_v) : "Nd" (port) ,##i ); return _v; } 
+#endif /* CONFIG_MULTIQUAD */
+
+#define __INS(s) \
+static inline void ins##s(unsigned short port, void * addr, unsigned long count) \
+{ __asm__ __volatile__ ("rep ; ins" #s \
+: "=D" (addr), "=c" (count) : "d" (port),"0" (addr),"1" (count)); }
+
+#define __OUTS(s) \
+static inline void outs##s(unsigned short port, const void * addr, unsigned long count) \
+{ __asm__ __volatile__ ("rep ; outs" #s \
+: "=S" (addr), "=c" (count) : "d" (port),"0" (addr),"1" (count)); }
+
+#define RETURN_TYPE unsigned char
+__IN(b,"")
+#undef RETURN_TYPE
+#define RETURN_TYPE unsigned short
+__IN(w,"")
+#undef RETURN_TYPE
+#define RETURN_TYPE unsigned int
+__IN(l,"")
+#undef RETURN_TYPE
+
+__OUT(b,"b",char)
+__OUT(w,"w",short)
+__OUT(l,,int)
+
+__INS(b)
+__INS(w)
+__INS(l)
+
+__OUTS(b)
+__OUTS(w)
+__OUTS(l)
 
 #endif
diff -urN virgin-2.4.10/include/asm-i386/mpspec.h numa-2.4.10/include/asm-i386/mpspec.h
--- virgin-2.4.10/include/asm-i386/mpspec.h	Tue Sep 18 11:21:04 2001
+++ numa-2.4.10/include/asm-i386/mpspec.h	Wed Sep 26 16:04:35 2001
@@ -156,7 +156,7 @@
  *	7	2 CPU MCA+PCI
  */
 
-#define MAX_IRQ_SOURCES 128
+#define MAX_IRQ_SOURCES 256
 #define MAX_MP_BUSSES 32
 enum mp_bustype {
 	MP_BUS_ISA = 1,
@@ -167,7 +167,7 @@
 extern int mp_bus_id_to_type [MAX_MP_BUSSES];
 extern int mp_bus_id_to_pci_bus [MAX_MP_BUSSES];
 
-extern unsigned int boot_cpu_id;
+extern unsigned int boot_cpu_physical_apicid;
 extern unsigned long phys_cpu_present_map;
 extern int smp_found_config;
 extern void find_smp_config (void);
diff -urN virgin-2.4.10/include/asm-i386/smp.h numa-2.4.10/include/asm-i386/smp.h
--- virgin-2.4.10/include/asm-i386/smp.h	Sun Sep 23 10:31:02 2001
+++ numa-2.4.10/include/asm-i386/smp.h	Wed Sep 26 16:04:35 2001
@@ -22,6 +22,18 @@
 #endif
 #endif
 
+#if CONFIG_SMP
+# ifdef CONFIG_MULTIQUAD
+#  define TARGET_CPUS 0xf     /* all CPUs in *THIS* quad */
+#  define INT_DELIVERY_MODE 0     /* physical delivery on LOCAL quad */
+# else
+#  define TARGET_CPUS cpu_online_map
+#  define INT_DELIVERY_MODE 1     /* logical delivery broadcast to all procs */
+# endif
+#else
+# define TARGET_CPUS 0x01
+#endif
+
 #ifdef CONFIG_SMP
 #ifndef ASSEMBLY
 
@@ -59,8 +71,21 @@
  * Some lowlevel functions might want to know about
  * the real APIC ID <-> CPU # mapping.
  */
-extern volatile int x86_apicid_to_cpu[NR_CPUS];
-extern volatile int x86_cpu_to_apicid[NR_CPUS];
+#define MAX_APICID 256
+extern volatile int cpu_to_physical_apicid[NR_CPUS];
+extern volatile int physical_apicid_to_cpu[MAX_APICID];
+extern volatile int cpu_to_logical_apicid[NR_CPUS];
+extern volatile int logical_apicid_to_cpu[MAX_APICID];
+
+#ifndef clustered_apic_mode
+ #ifdef CONFIG_MULTIQUAD
+  #define clustered_apic_mode (1)
+  #define esr_disable (1)
+ #else /* !CONFIG_MULTIQUAD */
+  #define clustered_apic_mode (0)
+  #define esr_disable (0)
+ #endif /* CONFIG_MULTIQUAD */
+#endif 
 
 /*
  * General functions that each host system must provide.
@@ -81,6 +106,12 @@
 {
 	/* we don't want to mark this access volatile - bad code generation */
 	return GET_APIC_ID(*(unsigned long *)(APIC_BASE+APIC_ID));
+}
+
+extern __inline int logical_smp_processor_id(void)
+{
+	/* we don't want to mark this access volatile - bad code generation */
+	return GET_APIC_LOGICAL_ID(*(unsigned long *)(APIC_BASE+APIC_LDR));
 }
 
 #endif /* !ASSEMBLY */
diff -urN virgin-2.4.10/include/asm-i386/smpboot.h numa-2.4.10/include/asm-i386/smpboot.h
--- virgin-2.4.10/include/asm-i386/smpboot.h	Wed Dec 31 16:00:00 1969
+++ numa-2.4.10/include/asm-i386/smpboot.h	Wed Sep 26 16:04:35 2001
@@ -0,0 +1,62 @@
+#ifndef __ASM_SMPBOOT_H
+#define __ASM_SMPBOOT_H
+
+#ifndef clustered_apic_mode
+ #ifdef CONFIG_MULTIQUAD
+  #define clustered_apic_mode (1)
+ #else /* !CONFIG_MULTIQUAD */
+  #define clustered_apic_mode (0)
+ #endif /* CONFIG_MULTIQUAD */
+#endif 
+ 
+#ifdef CONFIG_MULTIQUAD
+ #define TRAMPOLINE_LOW phys_to_virt(0x8)
+ #define TRAMPOLINE_HIGH phys_to_virt(0xa)
+#else /* !CONFIG_MULTIQUAD */
+ #define TRAMPOLINE_LOW phys_to_virt(0x467)
+ #define TRAMPOLINE_HIGH phys_to_virt(0x469)
+#endif /* CONFIG_MULTIQUAD */
+
+#ifdef CONFIG_MULTIQUAD
+ #define boot_cpu_apicid boot_cpu_logical_apicid
+#else /* !CONFIG_MULTIQUAD */
+ #define boot_cpu_apicid boot_cpu_physical_apicid
+#endif /* CONFIG_MULTIQUAD */
+
+/*
+ * How to map from the cpu_present_map
+ */
+#ifdef CONFIG_MULTIQUAD
+ #define cpu_present_to_apicid(mps_cpu) ( ((mps_cpu/4)*16) + (1<<(mps_cpu%4)) )
+#else /* !CONFIG_MULTIQUAD */
+ #define cpu_present_to_apicid(apicid) (apicid)
+#endif /* CONFIG_MULTIQUAD */
+
+/*
+ * Mappings between logical cpu number and logical / physical apicid
+ * The first four macros are trivial, but it keeps the abstraction consistent
+ */
+extern volatile int logical_apicid_2_cpu[];
+extern volatile int cpu_2_logical_apicid[];
+extern volatile int physical_apicid_2_cpu[];
+extern volatile int cpu_2_physical_apicid[];
+
+#define logical_apicid_to_cpu(apicid) logical_apicid_2_cpu[apicid]
+#define cpu_to_logical_apicid(cpu) cpu_2_logical_apicid[cpu]
+#define physical_apicid_to_cpu(apicid) physical_apicid_2_cpu[apicid]
+#define cpu_to_physical_apicid(cpu) cpu_2_physical_apicid[cpu]
+#ifdef CONFIG_MULTIQUAD			/* use logical IDs to bootstrap */
+#define boot_apicid_to_cpu(apicid) logical_apicid_2_cpu[apicid]
+#define cpu_to_boot_apicid(cpu) cpu_2_logical_apicid[cpu]
+#else /* !CONFIG_MULTIQUAD */		/* use physical IDs to bootstrap */
+#define boot_apicid_to_cpu(apicid) physical_apicid_2_cpu[apicid]
+#define cpu_to_boot_apicid(cpu) cpu_2_physical_apicid[cpu]
+#endif /* CONFIG_MULTIQUAD */
+
+
+#ifdef CONFIG_MULTIQUAD
+#else /* !CONFIG_MULTIQUAD */
+#endif /* CONFIG_MULTIQUAD */
+
+
+#endif
diff -urN virgin-2.4.10/init/main.c numa-2.4.10/init/main.c
--- virgin-2.4.10/init/main.c	Thu Sep 20 21:02:01 2001
+++ numa-2.4.10/init/main.c	Fri Sep 28 15:23:19 2001
@@ -519,7 +519,7 @@
 /*
  *	Activate the first processor.
  */
- 
+
 asmlinkage void __init start_kernel(void)
 {
 	char * command_line;
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/