[PATCH] local APIC support on uniprocessor machines

From: Keir Fraser (Keir.Fraser@cl.cam.ac.uk)
Date: Tue Aug 22 2000 - 08:57:19 EST


Thanks to Ben Redelings and Garst Reese for testing out my original
patch. The problem Garst was having was due to his ThinkPad containing
a mobile Pentium (ie. a P5 core). Uniprocessor P5 boards seem to
always disable the local APIC in hardware (P5 processors have an "APIC
enable" pin).

The attached patch is much more robust when it fails to find the
APIC. It's been tested successfully on:

 * a uniprocessor P5-120 (fails gracefully: APIC disabled)

 * uniprocessor Celeron and a uniprocessor PPro

 * multiprocessor PIII with SMP enabled, and SMP disabled

 * a uniprocessor PIII (by Ben Redelings)

 * a 380XD ThinkPad [mobile P5] (by Garst Reese ; fails gracefully)

The patch should have no effect on anything below a P5, so I think
that this is a fairly good range of tests aready. Tests on more
machines would obviously be welcome though :)

I'm aware of a separate "APIC timer" patch distributed as a
module (thank to John Levon for pointing this out!) . The problem with
this is that there could be a race between enabling and setting up the
APIC, and any impending external interrupts (which could get
lost). The only safe point at which to enable the APIC is therefore at
boot time (I think, although clever grubbing around with the
interrupt-pending register might avoid races).

 -- Keir Fraser

PS. Please "reply-all" as I don't subscribe to linux-kernel.

diff -urBP orig-2.4.0-test6/arch/i386/kernel/apic.c new-2.4.0-test6/arch/i386/kernel/apic.c
--- orig-2.4.0-test6/arch/i386/kernel/apic.c Mon Aug 14 14:27:58 2000
+++ new-2.4.0-test6/arch/i386/kernel/apic.c Mon Aug 14 14:28:24 2000
@@ -267,18 +267,16 @@
 {
         unsigned long apic_phys;
 
- if (smp_found_config) {
- apic_phys = mp_lapic_addr;
- } else {
- /*
- * set up a fake all zeroes page to simulate the
- * local APIC and another one for the IO-APIC. We
- * could use the real zero-page, but it's safer
- * this way if some buggy code writes to this page ...
- */
- apic_phys = (unsigned long) alloc_bootmem_pages(PAGE_SIZE);
- apic_phys = __pa(apic_phys);
- }
+ /*
+ * Now that we make more of an effort to enable the local APIC even
+ * when we could find no SMP config information, we always map the
+ * APIC registers correctly (ie. the zero-page trick has been
+ * removed). See `IO_APIC_init_uniprocessor' in io_apic.c for more
+ * info.
+ */
+ if ( !smp_found_config ) mp_lapic_addr = APIC_DEFAULT_PHYS_BASE;
+ apic_phys = mp_lapic_addr;
+
         set_fixmap_nocache(FIX_APIC_BASE, apic_phys);
         Dprintk("mapped APIC to %08lx (%08lx)\n", APIC_BASE, apic_phys);
 
diff -urBP orig-2.4.0-test6/arch/i386/kernel/io_apic.c new-2.4.0-test6/arch/i386/kernel/io_apic.c
--- orig-2.4.0-test6/arch/i386/kernel/io_apic.c Mon Aug 14 14:27:55 2000
+++ new-2.4.0-test6/arch/i386/kernel/io_apic.c Mon Aug 14 14:28:19 2000
@@ -1518,11 +1518,91 @@
  */
 void IO_APIC_init_uniprocessor (void)
 {
+ /*
+ * For real non-SMP machines, we now make more of an effort to
+ * actually enable the local APIC. We do this without enabling any IO
+ * APICs (without smp_config info we don't know if there are any!).
+ */
         if (!smp_found_config)
- return;
+ {
+ u32 h, l, dummy, features;
+
+ if ( boot_cpu_data.x86 < 5 )
+ {
+ printk("No local APIC on pre-Pentium Intel processors\n");
+ return;
+ }
+
+ if ( boot_cpu_data.x86 == 6 )
+ {
+ /*
+ * Some BIOSes disable the local APIC in the APIC_BASE MSR.
+ * This can only be done in software for PPro and above.
+ */
+ rdmsr(0x1b, l, h);
+ if ( !(l & 0x800) )
+ {
+ printk("Local APIC disabled by BIOS -- reenabling...\n");
+ l |= 0x800;
+ wrmsr(0x1b, l, h);
+
+ /* The APIC feature bit should now be enabled in `cpuid' */
+ cpuid(1, &dummy, &dummy, &dummy, &features);
+ if ( !(features & X86_FEATURE_APIC) )
+ {
+ printk("Could not enable APIC -- support disabled\n");
+ return;
+ }
+ }
+ }
+
+ /*
+ * Make sure that we are processor 0, and that we are indicated
+ * as present. This stuff doesn't normally get done on a
+ * uniprocessor machine.
+ */
+ phys_cpu_present_map = 1;
+ apic_read_around(APIC_ID); /* buggy P5 paranoia */
+ apic_write_around(APIC_ID, 0);
+
+ /*
+ * A final sanity check. Early P5 processors don't have a local
+ * APIC. Also, a P5 local APIC can be disabled in hardware!
+ */
+ if ( apic_read(APIC_ID) != 0 )
+ {
+ printk("Can't find local APIC: non-existent or disabled"
+ "in hardware\n");
+ return;
+ }
+
+ /* Now we know the APIC is enabled, update CPU features flags */
+ boot_cpu_data.x86_capability |= X86_FEATURE_APIC;
+
+ /*
+ * Not sure about this, but it stops external INTs being masked!
+ * This is necessary because we don't initialise any IO APICs:
+ * (a) they weren't memory-mapped by `init_apic_mappings' because
+ * it didn't find any smp configuration info.
+ * (b) in a uniprocessor system, an IO APIC is redundant because
+ * all external INTs must go to the only processor.
+ */
+ pic_mode = 1;
+ nr_ioapics = 0;
+ }
+
         connect_bsp_APIC();
         setup_local_APIC();
- setup_IO_APIC();
- setup_APIC_clocks();
+
+ /*
+ * We only set up IO APICs for a real SMP machine. Otherwise external
+ * INTs get masked locally, without them being routed to an IO APIC.
+ */
+ if ( smp_found_config )
+ {
+ setup_IO_APIC();
+ }
+
+ setup_APIC_clocks();
 }
 #endif

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



This archive was generated by hypermail 2b29 : Wed Aug 23 2000 - 21:00:06 EST