Subject: port-i386/36579: some improvements for the i386 cpu_reset() code
To: None <port-i386-maintainer@netbsd.org, gnats-admin@netbsd.org,>
From: Greg A. Woods <woods@planix.com>
List: netbsd-bugs
Date: 06/29/2007 19:40:01
>Number:         36579
>Category:       port-i386
>Synopsis:       some improvements for the i386 cpu_reset() code
>Confidential:   no
>Severity:       non-critical
>Priority:       medium
>Responsible:    port-i386-maintainer
>State:          open
>Class:          sw-bug
>Submitter-Id:   net
>Arrival-Date:   Fri Jun 29 19:40:00 +0000 2007
>Originator:     Greg A. Woods
>Release:        netbsd-4
>Organization:
Planix, Inc.; Toronto, Ontario; Canada
>Environment:
	
	
System: NetBSD 4.0BETA2
Architecture: i386
Machine: i386
>Description:



>How-To-Repeat:

	examine the related code in other operating systems

>Fix:

	warning: patch line numbers are off!

	see also the last few lines of change to cpu_reboot() in PR#
	port-i386/36578

	it might be quite helpful to add DEBUG or even DIAGNOSTIC
	printf()s to show which methods fail on a given machine....

Index: sys/arch/i386/i386/machdep.c
===================================================================
RCS file: /cvs/master/m-NetBSD/main/src/sys/arch/i386/i386/machdep.c,v
retrieving revision 1.586.2.4
diff -u -r1.586.2.4 machdep.c
--- sys/arch/i386/i386/machdep.c	20 Apr 2007 20:31:25 -0000	1.586.2.4
+++ sys/arch/i386/i386/machdep.c	29 Jun 2007 19:20:14 -0000
@@ -2184,10 +2212,48 @@
 #include <dev/ic/mc146818reg.h>		/* for NVRAM POST */
 #include <i386/isa/nvram.h>		/* for NVRAM POST */
 
+/* XXX some copied from sys/arch/x86/pci/pci_machdep.c, should be in <x86/include/pci_machdep.h> */
+/* XXX they're also used in sys/arch/i386/stand/lib/test/pci_user.c */
+#define PCI_MODE1_ENABLE	0x80000000UL
+#define PCI_MODE1_ADDRESS_REG	0x0cf8
+
+#define PCI_MODE1_RESET_CTL_REG	0x0cf9
+#define PCI_MODE1_DATA_REG	0x0cfc
+
+/*
+ * From Radisys 82600 High Integration Dual PCI System Controller Data Book:
+ * 
+ * Bits 1 and 2 in this register are used by the 82600 to generate a hard reset
+ * or a soft reset.  During a hard reset, the 82600 asserts CPURST# and LPRST#
+ * and resets its own core logic.  BPRST# is also asserted if the 82600 is
+ * configured as the BPCI Central Resource.  During a soft reset, the 82600
+ * only asserts INIT#.
+ *
+ * Bit	Description
+ *
+ * 7:3	Reserved.
+ *
+ * 2	Reset CPU (RCPU) ­ R/W. A transition of this bit from a 0 to a 1
+ *	initiates a reset. The type of reset is determined by bit 1.  This bit
+ *	cannot be read as a 1.
+ *
+ * 1	System Reset (SRST) ­ R/W.  This bit is used to select the type of
+ *	reset generated when bit 2 in this register transitions to a 1.  A
+ *	value of 1 selects a hard reset and 0 selects a soft reset
+ *
+ * 0	Reserved.
+ */
+#define PCI_RESET_RCPU		(1 << 2)
+#define PCI_RESET_SRST		(1 << 1)
+
+#define	PCAT_SYS_CTL_A		0x92	/* AT System Control Port A */
+#define PCAT_SYS_CTL_A_FRST	0x01	/* Fast Reset, aka Fast Init */
+
 void
 cpu_reset()
 {
 	struct region_descriptor region;
+	u_int8_t reg8;
 
 	disable_intr();
 
@@ -2213,15 +2279,50 @@
 	 * See AMD Geode SC1100 Processor Data Book, Revision 2.0,
 	 * sections 6.3.1, 6.3.2, and 6.4.1.
 	 */
-	if (cpu_info_primary.ci_signature == 0x540) {
-		outl(0xcf8, 0x80009044ul);
-		outl(0xcfc, 0xf);
+	if (cpu_info_primary.ci_signature == 0x540) { /* CPU_GEODE1100??? */
+		outl(PCI_MODE1_ADDRESS_REG, PCI_MODE1_ENABLE | 0x9044);
+		outl(PCI_MODE1_DATA_REG, 0xf);
+	}
+
+	/*
+	 * Try the PCI system & cpu reset _first_ (from FreeBSD and GNU/Linux)
+	 *
+	 * Write 0x6 to PCI Reset Control Register (0xcf9) to reset the CPU,
+	 * the PCI controller itself, and to trigger a system-wide reset.
+	 *
+	 * This is the best method for all recent and modern systems that
+	 * include any form of PCI controller.
+	 */
+	reg8 = inb(PCI_MODE1_RESET_CTL_REG);
+	reg8 |= PCI_RESET_RCPU | PCI_RESET_SRST;
+	outb(PCI_MODE1_RESET_CTL_REG, reg8);
+	delay(500000);		       /* wait 0.5 sec to see if that did it */
+
+	/*
+	 * Try reset by setting the 0x01 bit of the System Control Port A
+	 * (0x92) (also from FreeBSD)
+	 *
+	 * This is the second-best way to reset any i386 system, and should
+	 * work on everything back to the PC/AT.
+	 *
+	 * Note this doesn't work (or didn't) in VMware.
+	 */
+	reg8 = inb(PCAT_SYS_CTL_A);
+	/* Check the the hardware actually has the port in question */
+	if (reg8 != 0xff) {
+		/* FAST_INIT must be zero before a one can be written */
+		if ((reg8 & PCAT_SYS_CTL_A_FRST) != 0)
+			outb(PCAT_SYS_CTL_A, reg8 & ~PCAT_SYS_CTL_A_FRST);
+		outb(PCAT_SYS_CTL_A, reg8 | PCAT_SYS_CTL_A_FRST);
+		delay(500000);		/* wait 0.5 sec to see if that did it */
 	}
 
 	/*
-	 * The keyboard controller has 4 random output pins, one of which is
-	 * connected to the RESET pin on the CPU in many PCs.  We tell the
-	 * keyboard controller to pulse this line a couple of times.
+	 * The keyboard controller has 4 output pins, one of which is connected
+	 * to the CPU RESET line in many PCs.  We tell the keyboard controller
+	 * to pulse this line a couple of times.
+	 *
+	 * XXX FreeBSD only does it once, then waits for 0.5 sec
 	 */
 	outb(IO_KBD + KBCMDP, KBC_PULSE0);
 	delay(100000);
@@ -2237,7 +2338,7 @@
 	lidt(&region);
 	__asm volatile("divl %0,%1" : : "q" (0), "a" (0));
 
-#if 0
+#if 0 /* XXX FreeBSD actually does resort to this as a last go */
 	/*
 	 * Try to cause a triple fault and watchdog reset by unmapping the
 	 * entire address space and doing a TLB flush.
@@ -2246,7 +2347,6 @@
 	tlbflush();
 #endif
 
-	for (;;);
 }
 
 void

>Unformatted: