Skip site navigation (1)Skip section navigation (2)
Date:      Tue, 9 Jun 2009 04:17:36 +0000 (UTC)
From:      Ariff Abdullah <ariff@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r193804 - in head/sys: amd64/amd64 i386/i386
Message-ID:  <200906090417.n594Ham6055384@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: ariff
Date: Tue Jun  9 04:17:36 2009
New Revision: 193804
URL: http://svn.freebsd.org/changeset/base/193804

Log:
  Move C1E workaround into its own idle function. Previous workaround works
  only during initial booting process, while there are laptops/BIOSes that
  tend to act 'smarter' by force enabling C1E if the main power adapter
  being pulled out, rendering previous workaround ineffective. Given the
  fact that we still rely on local APIC to drive timer interrupt, this
  workaround should keep all Turion (probably Phenom too) X\d+ alive whether
  its on battery power or not.
  
  URL:		http://lists.freebsd.org/pipermail/freebsd-acpi/2008-April/004858.html
      		http://lists.freebsd.org/pipermail/freebsd-acpi/2008-May/004888.html
  
  Tested by:	Peter Jeremy <peterjeremy at optushome d com d au>

Modified:
  head/sys/amd64/amd64/local_apic.c
  head/sys/amd64/amd64/machdep.c
  head/sys/i386/i386/local_apic.c
  head/sys/i386/i386/machdep.c

Modified: head/sys/amd64/amd64/local_apic.c
==============================================================================
--- head/sys/amd64/amd64/local_apic.c	Tue Jun  9 04:17:08 2009	(r193803)
+++ head/sys/amd64/amd64/local_apic.c	Tue Jun  9 04:17:36 2009	(r193804)
@@ -329,29 +329,6 @@ lapic_setup(int boot)
 
 	/* XXX: Error and thermal LVTs */
 
-	if (cpu_vendor_id == CPU_VENDOR_AMD) {
-		/*
-		 * Detect the presence of C1E capability mostly on latest
-		 * dual-cores (or future) k8 family.  This feature renders
-		 * the local APIC timer dead, so we disable it by reading
-		 * the Interrupt Pending Message register and clearing both
-		 * C1eOnCmpHalt (bit 28) and SmiOnCmpHalt (bit 27).
-		 * 
-		 * Reference:
-		 *   "BIOS and Kernel Developer's Guide for AMD NPT
-		 *    Family 0Fh Processors"
-		 *   #32559 revision 3.00
-		 */
-		if ((cpu_id & 0x00000f00) == 0x00000f00 &&
-		    (cpu_id & 0x0fff0000) >=  0x00040000) {
-			uint64_t msr;
-
-			msr = rdmsr(0xc0010055);
-			if (msr & 0x18000000)
-				wrmsr(0xc0010055, msr & ~0x18000000ULL);
-		}
-	}
-
 	intr_restore(eflags);
 }
 

Modified: head/sys/amd64/amd64/machdep.c
==============================================================================
--- head/sys/amd64/amd64/machdep.c	Tue Jun  9 04:17:08 2009	(r193803)
+++ head/sys/amd64/amd64/machdep.c	Tue Jun  9 04:17:36 2009	(r193804)
@@ -600,6 +600,69 @@ cpu_idle_acpi(int busy)
 		__asm __volatile("sti; hlt");
 }
 
+static int cpu_ident_amdc1e = 0;
+
+static int
+cpu_probe_amdc1e(void)
+{
+	int i;
+
+	/*
+	 * Forget it, if we're not using local APIC timer.
+	 */
+	if (resource_disabled("apic", 0) ||
+	    (resource_int_value("apic", 0, "clock", &i) == 0 && i == 0))
+		return (0);
+
+	/*
+	 * Detect the presence of C1E capability mostly on latest
+	 * dual-cores (or future) k8 family.
+	 */
+	if (cpu_vendor_id == CPU_VENDOR_AMD &&
+	    (cpu_id & 0x00000f00) == 0x00000f00 &&
+	    (cpu_id & 0x0fff0000) >=  0x00040000) {
+		cpu_ident_amdc1e = 1;
+		return (1);
+	}
+
+	return (0);
+}
+
+/*
+ * C1E renders the local APIC timer dead, so we disable it by
+ * reading the Interrupt Pending Message register and clearing
+ * both C1eOnCmpHalt (bit 28) and SmiOnCmpHalt (bit 27).
+ * 
+ * Reference:
+ *   "BIOS and Kernel Developer's Guide for AMD NPT Family 0Fh Processors"
+ *   #32559 revision 3.00+
+ */
+#define	MSR_AMDK8_IPM		0xc0010055
+#define	AMDK8_SMIONCMPHALT	(1ULL << 27)
+#define	AMDK8_C1EONCMPHALT	(1ULL << 28)
+#define	AMDK8_CMPHALT		(AMDK8_SMIONCMPHALT | AMDK8_C1EONCMPHALT)
+
+static void
+cpu_idle_amdc1e(int busy)
+{
+
+	disable_intr();
+	if (sched_runnable())
+		enable_intr();
+	else {
+		uint64_t msr;
+
+		msr = rdmsr(MSR_AMDK8_IPM);
+		if (msr & AMDK8_CMPHALT)
+			wrmsr(MSR_AMDK8_IPM, msr & ~AMDK8_CMPHALT);
+
+		if (cpu_idle_hook)
+			cpu_idle_hook();
+		else
+			__asm __volatile("sti; hlt");
+	}
+}
+
 static void
 cpu_idle_spin(int busy)
 {
@@ -697,6 +760,7 @@ struct {
 	{ cpu_idle_spin, "spin" },
 	{ cpu_idle_mwait, "mwait" },
 	{ cpu_idle_mwait_hlt, "mwait_hlt" },
+	{ cpu_idle_amdc1e, "amdc1e" },
 	{ cpu_idle_hlt, "hlt" },
 	{ cpu_idle_acpi, "acpi" },
 	{ NULL, NULL }
@@ -715,6 +779,9 @@ idle_sysctl_available(SYSCTL_HANDLER_ARG
 		if (strstr(idle_tbl[i].id_name, "mwait") &&
 		    (cpu_feature2 & CPUID2_MON) == 0)
 			continue;
+		if (strcmp(idle_tbl[i].id_name, "amdc1e") == 0 &&
+		    cpu_ident_amdc1e == 0)
+			continue;
 		p += sprintf(p, "%s, ", idle_tbl[i].id_name);
 	}
 	error = sysctl_handle_string(oidp, avail, 0, req);
@@ -745,6 +812,9 @@ idle_sysctl(SYSCTL_HANDLER_ARGS)
 		if (strstr(idle_tbl[i].id_name, "mwait") &&
 		    (cpu_feature2 & CPUID2_MON) == 0)
 			continue;
+		if (strcmp(idle_tbl[i].id_name, "amdc1e") == 0 &&
+		    cpu_ident_amdc1e == 0)
+			continue;
 		if (strcmp(idle_tbl[i].id_name, buf))
 			continue;
 		cpu_idle_fn = idle_tbl[i].id_fn;
@@ -1593,6 +1663,9 @@ hammer_time(u_int64_t modulep, u_int64_t
 	}
 #endif
 
+	if (cpu_probe_amdc1e())
+		cpu_idle_fn = cpu_idle_amdc1e;
+
 	/* Location of kernel stack for locore */
 	return ((u_int64_t)thread0.td_pcb);
 }

Modified: head/sys/i386/i386/local_apic.c
==============================================================================
--- head/sys/i386/i386/local_apic.c	Tue Jun  9 04:17:08 2009	(r193803)
+++ head/sys/i386/i386/local_apic.c	Tue Jun  9 04:17:36 2009	(r193804)
@@ -331,29 +331,6 @@ lapic_setup(int boot)
 
 	/* XXX: Error and thermal LVTs */
 
-	if (cpu_vendor_id == CPU_VENDOR_AMD) {
-		/*
-		 * Detect the presence of C1E capability mostly on latest
-		 * dual-cores (or future) k8 family.  This feature renders
-		 * the local APIC timer dead, so we disable it by reading
-		 * the Interrupt Pending Message register and clearing both
-		 * C1eOnCmpHalt (bit 28) and SmiOnCmpHalt (bit 27).
-		 * 
-		 * Reference:
-		 *   "BIOS and Kernel Developer's Guide for AMD NPT
-		 *    Family 0Fh Processors"
-		 *   #32559 revision 3.00
-		 */
-		if ((cpu_id & 0x00000f00) == 0x00000f00 &&
-		    (cpu_id & 0x0fff0000) >=  0x00040000) {
-			uint64_t msr;
-
-			msr = rdmsr(0xc0010055);
-			if (msr & 0x18000000)
-				wrmsr(0xc0010055, msr & ~0x18000000ULL);
-		}
-	}
-
 	intr_restore(eflags);
 }
 

Modified: head/sys/i386/i386/machdep.c
==============================================================================
--- head/sys/i386/i386/machdep.c	Tue Jun  9 04:17:08 2009	(r193803)
+++ head/sys/i386/i386/machdep.c	Tue Jun  9 04:17:36 2009	(r193804)
@@ -1231,6 +1231,70 @@ cpu_idle_acpi(int busy)
 		__asm __volatile("sti; hlt");
 }
 
+static int cpu_ident_amdc1e = 0;
+
+static int
+cpu_probe_amdc1e(void)
+{ 
+#ifdef DEV_APIC
+	int i;
+
+	/*
+	 * Forget it, if we're not using local APIC timer.
+	 */
+	if (resource_disabled("apic", 0) ||
+	    (resource_int_value("apic", 0, "clock", &i) == 0 && i == 0))
+		return (0);
+
+	/*
+	 * Detect the presence of C1E capability mostly on latest
+	 * dual-cores (or future) k8 family.
+	 */
+	if (cpu_vendor_id == CPU_VENDOR_AMD &&
+	    (cpu_id & 0x00000f00) == 0x00000f00 &&
+	    (cpu_id & 0x0fff0000) >=  0x00040000) {
+		cpu_ident_amdc1e = 1;
+		return (1);
+	}
+#endif
+	return (0);
+}
+
+/*
+ * C1E renders the local APIC timer dead, so we disable it by
+ * reading the Interrupt Pending Message register and clearing
+ * both C1eOnCmpHalt (bit 28) and SmiOnCmpHalt (bit 27).
+ * 
+ * Reference:
+ *   "BIOS and Kernel Developer's Guide for AMD NPT Family 0Fh Processors"
+ *   #32559 revision 3.00+
+ */
+#define	MSR_AMDK8_IPM		0xc0010055
+#define	AMDK8_SMIONCMPHALT	(1ULL << 27)
+#define	AMDK8_C1EONCMPHALT	(1ULL << 28)
+#define	AMDK8_CMPHALT		(AMDK8_SMIONCMPHALT | AMDK8_C1EONCMPHALT)
+
+static void
+cpu_idle_amdc1e(int busy)
+{
+
+	disable_intr();
+	if (sched_runnable())
+		enable_intr();
+	else {
+		uint64_t msr;
+
+		msr = rdmsr(MSR_AMDK8_IPM);
+		if (msr & AMDK8_CMPHALT)
+			wrmsr(MSR_AMDK8_IPM, msr & ~AMDK8_CMPHALT);
+
+		if (cpu_idle_hook)
+			cpu_idle_hook();
+		else
+			__asm __volatile("sti; hlt");
+	}
+}
+
 static void
 cpu_idle_spin(int busy)
 {
@@ -1332,6 +1396,7 @@ struct {
 	{ cpu_idle_spin, "spin" },
 	{ cpu_idle_mwait, "mwait" },
 	{ cpu_idle_mwait_hlt, "mwait_hlt" },
+	{ cpu_idle_amdc1e, "amdc1e" },
 	{ cpu_idle_hlt, "hlt" },
 	{ cpu_idle_acpi, "acpi" },
 	{ NULL, NULL }
@@ -1350,6 +1415,9 @@ idle_sysctl_available(SYSCTL_HANDLER_ARG
 		if (strstr(idle_tbl[i].id_name, "mwait") &&
 		    (cpu_feature2 & CPUID2_MON) == 0)
 			continue;
+		if (strcmp(idle_tbl[i].id_name, "amdc1e") == 0 &&
+		    cpu_ident_amdc1e == 0)
+			continue;
 		p += sprintf(p, "%s, ", idle_tbl[i].id_name);
 	}
 	error = sysctl_handle_string(oidp, avail, 0, req);
@@ -1380,6 +1448,9 @@ idle_sysctl(SYSCTL_HANDLER_ARGS)
 		if (strstr(idle_tbl[i].id_name, "mwait") &&
 		    (cpu_feature2 & CPUID2_MON) == 0)
 			continue;
+		if (strcmp(idle_tbl[i].id_name, "amdc1e") == 0 &&
+		    cpu_ident_amdc1e == 0)
+			continue;
 		if (strcmp(idle_tbl[i].id_name, buf))
 			continue;
 		cpu_idle_fn = idle_tbl[i].id_fn;
@@ -2583,6 +2654,9 @@ init386(first)
 	thread0.td_frame = &proc0_tf;
 	thread0.td_pcb->pcb_fsd = PCPU_GET(fsgs_gdt)[0];
 	thread0.td_pcb->pcb_gsd = PCPU_GET(fsgs_gdt)[1];
+
+	if (cpu_probe_amdc1e())
+		cpu_idle_fn = cpu_idle_amdc1e;
 }
 
 #else
@@ -2847,6 +2921,9 @@ init386(first)
 #endif
 	thread0.td_pcb->pcb_ext = 0;
 	thread0.td_frame = &proc0_tf;
+
+	if (cpu_probe_amdc1e())
+		cpu_idle_fn = cpu_idle_amdc1e;
 }
 #endif
 



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200906090417.n594Ham6055384>