Skip site navigation (1)Skip section navigation (2)
Date:      Fri, 18 May 2012 18:55:59 +0000 (UTC)
From:      Mitsuru IWASAKI <iwasaki@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r235622 - in head/sys: i386/acpica i386/i386 i386/include kern pc98/pc98 sys
Message-ID:  <201205181855.q4IItxs0069711@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: iwasaki
Date: Fri May 18 18:55:58 2012
New Revision: 235622
URL: http://svn.freebsd.org/changeset/base/235622

Log:
  Add SMP/i386 suspend/resume support.
  Most part is merged from amd64.
  
  - i386/acpica/acpi_wakecode.S
  Replaced with amd64 code (from realmode to paging enabling code).
  
  - i386/acpica/acpi_wakeup.c
  Replaced with amd64 code (except for wakeup_pagetables stuff).
  
  - i386/include/pcb.h
  - i386/i386/genassym.c
  Added PCB new members (CR0, CR2, CR4, DS, ED, FS, SS, GDT, IDT, LDT
  and TR) needed for suspend/resume, not for context switch.
  
  - i386/i386/swtch.s
  Added suspendctx() and resumectx().
  Note that savectx() was not changed and used for suspending (while
  amd64 code uses it).
  BSP and AP execute the same sequence, suspendctx(), acpi_wakecode()
  and resumectx() for suspend/resume (in case of UP system also).
  
  - i386/i386/apic_vector.s
  Added cpususpend().
  
  - i386/i386/mp_machdep.c
  - i386/include/smp.h
  Added cpususpend_handler().
  
  - i386/include/apicvar.h
  - kern/subr_smp.c
  - sys/smp.h
  Added IPI_SUSPEND and suspend_cpus().
  
  - i386/i386/initcpu.c
  - i386/i386/machdep.c
  - i386/include/md_var.h
  - pc98/pc98/machdep.c
  Moved initializecpu() declarations to md_var.h.
  
  MFC after:	3 days

Modified:
  head/sys/i386/acpica/acpi_wakecode.S
  head/sys/i386/acpica/acpi_wakeup.c
  head/sys/i386/i386/apic_vector.s
  head/sys/i386/i386/genassym.c
  head/sys/i386/i386/initcpu.c
  head/sys/i386/i386/machdep.c
  head/sys/i386/i386/mp_machdep.c
  head/sys/i386/i386/swtch.s
  head/sys/i386/include/apicvar.h
  head/sys/i386/include/md_var.h
  head/sys/i386/include/pcb.h
  head/sys/i386/include/smp.h
  head/sys/kern/subr_smp.c
  head/sys/pc98/pc98/machdep.c
  head/sys/sys/smp.h

Modified: head/sys/i386/acpica/acpi_wakecode.S
==============================================================================
--- head/sys/i386/acpica/acpi_wakecode.S	Fri May 18 18:53:28 2012	(r235621)
+++ head/sys/i386/acpica/acpi_wakecode.S	Fri May 18 18:55:58 2012	(r235622)
@@ -1,6 +1,8 @@
 /*-
  * Copyright (c) 2001 Takanori Watanabe <takawata@jp.freebsd.org>
- * Copyright (c) 2001 Mitsuru IWASAKI <iwasaki@jp.freebsd.org>
+ * Copyright (c) 2001-2012 Mitsuru IWASAKI <iwasaki@jp.freebsd.org>
+ * Copyright (c) 2003 Peter Wemm
+ * Copyright (c) 2008-2012 Jung-uk Kim <jkim@FreeBSD.org>
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -28,7 +30,9 @@
  */
 
 #include <machine/asmacros.h>
+#include <machine/ppireg.h>
 #include <machine/specialreg.h>
+#include <machine/timerreg.h>
 
 #include "assym.s"
 
@@ -39,221 +43,163 @@
  * Depending on the previous sleep state, we may need to initialize more
  * of the system (i.e., S3 suspend-to-RAM vs. S4 suspend-to-disk).
  */
-	.align 4
-	.code16
-wakeup_16:
-	nop
-	cli
-	cld
 
+	.data				/* So we can modify it */
+
+	ALIGN_TEXT
+	.code16
+wakeup_start:
 	/*
 	 * Set up segment registers for real mode, a small stack for
 	 * any calls we make, and clear any flags.
 	 */
-	movw	%cs,%ax
-	movw	%ax,%ds
-	movw	%ax,%ss
-	movw	$PAGE_SIZE,%sp
-	pushl	$0
-	popfl
+	cli				/* make sure no interrupts */
+	mov	%cs, %ax		/* copy %cs to %ds.  Remember these */
+	mov	%ax, %ds		/* are offsets rather than selectors */
+	mov	%ax, %ss
+	movw	$PAGE_SIZE, %sp
+	xorw	%ax, %ax
+	pushw	%ax
+	popfw
 
 	/* To debug resume hangs, beep the speaker if the user requested. */
-	cmpl	$1,resume_beep
-	jne	nobeep
-	movb	$0xc0,%al
-	outb	%al,$0x42
-	movb	$0x04,%al
-	outb	%al,$0x42
-	inb	$0x61,%al
-	orb	$0x3,%al
-	outb	%al,$0x61
-nobeep:
+	testb	$~0, resume_beep - wakeup_start
+	jz	1f
+	movb	$0, resume_beep - wakeup_start
+
+	/* Set PIC timer2 to beep. */
+	movb	$(TIMER_SEL2 | TIMER_SQWAVE | TIMER_16BIT), %al
+	outb	%al, $TIMER_MODE
+
+	/* Turn on speaker. */
+	inb	$IO_PPI, %al
+	orb	$PIT_SPKR, %al
+	outb	%al, $IO_PPI
+
+	/* Set frequency. */
+	movw	$0x4c0, %ax
+	outb	%al, $TIMER_CNTR2
+	shrw	$8, %ax
+	outb	%al, $TIMER_CNTR2
+1:
 
 	/* Re-initialize video BIOS if the reset_video tunable is set. */
-	cmpl	$1,reset_video
-	jne	nobiosreset
-	lcall	$0xc000,$3
+	testb	$~0, reset_video - wakeup_start
+	jz	1f
+	movb	$0, reset_video - wakeup_start
+	lcall	$0xc000, $3
+
+	/* When we reach here, int 0x10 should be ready.  Hide cursor. */
+	movb	$0x01, %ah
+	movb	$0x20, %ch
+	int	$0x10
+
+	/* Re-start in case the previous BIOS call clobbers them. */
+	jmp	wakeup_start
+1:
 
 	/*
-	 * Set up segment registers for real mode again in case the
-	 * previous BIOS call clobbers them.
+	 * Find relocation base and patch the gdt descript and ljmp targets
 	 */
-	movw	%cs,%ax
-	movw	%ax,%ds
-	movw	%ax,%ss
-nobiosreset:
-
-	/* Load GDT for real mode.  Use 32 bit prefix for addresses >16 MB. */
-	lgdtl	physical_gdt
-
-	/* Restore CR2, CR3 and CR4 */
-	movl	previous_cr2,%eax
-	movl	%eax,%cr2
-	movl	previous_cr3,%eax
-	movl	%eax,%cr3
-	movl	previous_cr4,%eax
-	movl	%eax,%cr4
-
-	/* Transfer some values to protected mode with an inline stack */
-#define NVALUES	9
-#define TRANSFER_STACK32(val, idx)	\
-	movl	val,%eax;		\
-	movl	%eax,wakeup_32stack+(idx+1)+(idx*4)
-
-	TRANSFER_STACK32(previous_ss,		(NVALUES - 9))
-	TRANSFER_STACK32(previous_fs,		(NVALUES - 8))
-	TRANSFER_STACK32(previous_ds,		(NVALUES - 7))
-	TRANSFER_STACK32(physical_gdt+2,	(NVALUES - 6))
-	TRANSFER_STACK32(where_to_recover,	(NVALUES - 5))
-	TRANSFER_STACK32(previous_idt+2,	(NVALUES - 4))
-	TRANSFER_STACK32(previous_ldt,		(NVALUES - 3))
-	TRANSFER_STACK32(previous_gdt+2,	(NVALUES - 2))
-	TRANSFER_STACK32(previous_tr,		(NVALUES - 1))
-	TRANSFER_STACK32(previous_cr0,		(NVALUES - 0))
+	xorl	%ebx, %ebx
+	mov	%cs, %bx
+	sall	$4, %ebx		/* %ebx is now our relocation base */
 
-	mov	physical_esp,%esi	/* to be used in 32bit code */
+	/*
+	 * Load the descriptor table pointer.  We'll need it when running
+	 * in 16-bit protected mode.
+	 */
+	lgdtl	bootgdtdesc - wakeup_start
 
 	/* Enable protected mode */
-	movl	%cr0,%eax
-	orl	$(CR0_PE),%eax
-	movl	%eax,%cr0
+	movl	$CR0_PE, %eax
+	mov	%eax, %cr0
 
+	/*
+	 * Now execute a far jump to turn on protected mode.  This
+	 * causes the segment registers to turn into selectors and causes
+	 * %cs to be loaded from the gdt.
+	 *
+	 * The following instruction is:
+	 * ljmpl $bootcode32 - bootgdt, $wakeup_32 - wakeup_start
+	 * but gas cannot assemble that.  And besides, we patch the targets
+	 * in early startup and its a little clearer what we are patching.
+	 */
 wakeup_sw32:
-	/* Switch to protected mode by intersegmental jump */
-	ljmpl	$KCSEL,$0x12345678	/* Code location, to be replaced */
+	.byte	0x66			/* size override to 32 bits */
+	.byte	0xea			/* opcode for far jump */
+	.long	wakeup_32 - wakeup_start /* offset in segment */
+	.word	bootcode32 - bootgdt	/* index in gdt for 32 bit code */
 
 	/*
-	 * Now switched to protected mode without paging enabled.
-	 *	%esi: KERNEL stack pointer (physical address)
+	 * At this point, we are running in 32 bit legacy protected mode.
 	 */
+	ALIGN_TEXT
 	.code32
 wakeup_32:
-	nop
 
-	/* Set up segment registers for protected mode */
-	movw	$KDSEL,%ax		/* KDSEL to segment registers */
-	movw	%ax,%ds
-	movw	%ax,%es
-	movw	%ax,%gs
-	movw	%ax,%ss
-	movw	$KPSEL,%ax		/* KPSEL to %fs */
-	movw	%ax,%fs
-	movl	%esi,%esp		/* physical address stack pointer */
-
-wakeup_32stack:
-	/* Operands are overwritten in 16 bit code by TRANSFER_STACK32 macro */
-	pushl	$0xabcdef09		/* ss + dummy */
-	pushl	$0xabcdef08		/* fs + gs */
-	pushl	$0xabcdef07		/* ds + es */
-	pushl	$0xabcdef06		/* gdt:base (physical address) */
-	pushl	$0xabcdef05		/* recover address */
-	pushl	$0xabcdef04		/* idt:base */
-	pushl	$0xabcdef03		/* ldt + idt:limit */
-	pushl	$0xabcdef02		/* gdt:base */
-	pushl	$0xabcdef01		/* TR + gdt:limit */
-	pushl	$0xabcdef00		/* CR0 */
-
-	movl	%esp,%ebp
-#define CR0_REGISTER		0(%ebp)
-#define TASK_REGISTER		4(%ebp)
-#define PREVIOUS_GDT		6(%ebp)
-#define PREVIOUS_LDT		12(%ebp)
-#define PREVIOUS_IDT		14(%ebp)
-#define RECOVER_ADDR		20(%ebp)
-#define PHYSICAL_GDT_BASE	24(%ebp)
-#define PREVIOUS_DS		28(%ebp)
-#define PREVIOUS_ES		30(%ebp)
-#define PREVIOUS_FS		32(%ebp)
-#define PREVIOUS_GS		34(%ebp)
-#define PREVIOUS_SS		36(%ebp)
-
-	/* Fixup TSS type field */
-#define TSS_TYPEFIX_MASK	0xf9
-	xorl	%esi,%esi
-	movl	PHYSICAL_GDT_BASE,%ebx
-	movw	TASK_REGISTER,%si
-	leal	(%ebx,%esi),%eax	/* get TSS segment descriptor */
-	andb	$TSS_TYPEFIX_MASK,5(%eax)
-
-	/* Prepare to return to sleep/wakeup code point */
-	lgdtl	PREVIOUS_GDT
-	lidtl	PREVIOUS_IDT
-
-	/* Pack values from the GDT to be loaded into segment registers. */
-	movl	PREVIOUS_DS,%ebx
-	movl	PREVIOUS_FS,%ecx
-	movl	PREVIOUS_SS,%edx
-	movw	TASK_REGISTER,%si
-	shll	$16,%esi
-	movw	PREVIOUS_LDT,%si
-	movl	RECOVER_ADDR,%edi
-
-	/* Enable paging and etc. */
-	movl	CR0_REGISTER,%eax
-	movl	%eax,%cr0
+	mov	$bootdata32 - bootgdt, %eax
+	mov	%ax, %ds
 
-	/* Flush the prefetch queue */
-	jmp	1f
-1:	jmp	1f
-1:
+	/* Get PCB and return address. */
+	movl	wakeup_pcb - wakeup_start(%ebx), %esi
+	movl	wakeup_ret - wakeup_start(%ebx), %edi
+
+	/* Restore CR4 and CR3. */
+	movl	wakeup_cr4 - wakeup_start(%ebx), %eax
+	mov	%eax, %cr4
+	movl	wakeup_cr3 - wakeup_start(%ebx), %eax
+	mov	%eax, %cr3
 
 	/*
-	 * Now we are in kernel virtual memory addressing with the following
-	 * original register values:
-	 *	%ebx: ds + es
-	 *	%ecx: fs + gs
-	 *	%edx: ss + dummy
-	 *	%esi: LDTR + TR
-	 *	%edi: recover address
-	 * We'll load these back into the segment registers now.
+	 * Finally, switch to long bit mode by enabling paging.  We have
+	 * to be very careful here because all the segmentation disappears
+	 * out from underneath us.  The spec says we can depend on the
+	 * subsequent pipelined branch to execute, but *only if* everthing
+	 * is still identity mapped.  If any mappings change, the pipeline
+	 * will flush.
 	 */
-	nop
-
-	movl	%esi,%eax		/* LDTR + TR */
-	lldt	%ax			/* load LDT register */
-	shrl	$16,%eax
-	ltr	%ax			/* load task register */
-
-	/* Restore segment registers */
-	movl	%ebx,%eax		/* ds + es */
-	movw	%ax,%ds
-	shrl	$16,%eax
-	movw	%ax,%es
-	movl	%ecx,%eax		/* fs + gs */
-	movw	%ax,%fs
-	shrl	$16,%eax
-	movw	%ax,%gs
-	movl	%edx,%eax		/* ss */
-	movw	%ax,%ss
+	mov	%cr0, %eax
+	orl	$CR0_PG, %eax
+	mov	%eax, %cr0
 
-	/* Jump to acpi_restorecpu() */
+	jmp	1f
+1:
+	/* Jump to return address. */
 	jmp	*%edi
 
-/* used in real mode */
-physical_gdt:		.word 0
-			.long 0
-physical_esp:		.long 0
-previous_cr2:		.long 0
-previous_cr3:		.long 0
-previous_cr4:		.long 0
-resume_beep:		.long 0
-reset_video:		.long 0
+	.data
 
-/*
- * Transfer from real mode to protected mode.  The order of these variables
- * is very important, DO NOT INSERT OR CHANGE unless you know why.
- */
-previous_cr0:		.long 0
-previous_tr:		.word 0
-previous_gdt:		.word 0
-			.long 0
-previous_ldt:		.word 0
-previous_idt:		.word 0
-			.long 0
-where_to_recover:	.long 0
-previous_ds:		.word 0
-previous_es:		.word 0
-previous_fs:		.word 0
-previous_gs:		.word 0
-previous_ss:		.word 0
-dummy:			.word 0
+resume_beep:
+	.byte	0
+reset_video:
+	.byte	0
+
+	ALIGN_DATA
+bootgdt:
+	.long	0x00000000
+	.long	0x00000000
+
+bootcode32:
+	.long	0x0000ffff
+	.long	0x00cf9b00
+
+bootdata32:
+	.long	0x0000ffff
+	.long	0x00cf9300
+bootgdtend:
+
+bootgdtdesc:
+	.word	bootgdtend - bootgdt	/* Length */
+	.long	bootgdt - wakeup_start	/* Offset plus %ds << 4 */
+
+	ALIGN_DATA
+wakeup_cr4:
+	.long	0
+wakeup_cr3:
+	.long	0
+wakeup_pcb:
+	.long	0
+wakeup_ret:
+	.long	0
+dummy:

Modified: head/sys/i386/acpica/acpi_wakeup.c
==============================================================================
--- head/sys/i386/acpica/acpi_wakeup.c	Fri May 18 18:53:28 2012	(r235621)
+++ head/sys/i386/acpica/acpi_wakeup.c	Fri May 18 18:55:58 2012	(r235622)
@@ -1,6 +1,8 @@
 /*-
  * Copyright (c) 2001 Takanori Watanabe <takawata@jp.freebsd.org>
- * Copyright (c) 2001 Mitsuru IWASAKI <iwasaki@jp.freebsd.org>
+ * Copyright (c) 2001-2012 Mitsuru IWASAKI <iwasaki@jp.freebsd.org>
+ * Copyright (c) 2003 Peter Wemm
+ * Copyright (c) 2008-2012 Jung-uk Kim <jkim@FreeBSD.org>
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -29,26 +31,29 @@
 __FBSDID("$FreeBSD$");
 
 #include <sys/param.h>
-#include <sys/systm.h>
-#include <sys/kernel.h>
 #include <sys/bus.h>
-#include <sys/lock.h>
+#include <sys/eventhandler.h>
+#include <sys/kernel.h>
 #include <sys/malloc.h>
 #include <sys/memrange.h>
-#include <sys/proc.h>
-#include <sys/sysctl.h>
+#include <sys/smp.h>
 
 #include <vm/vm.h>
 #include <vm/pmap.h>
-#include <vm/vm_object.h>
-#include <vm/vm_page.h>
-#include <vm/vm_map.h>
 
-#include <machine/bus.h>
-#include <machine/cpufunc.h>
+#include <machine/clock.h>
 #include <machine/intr_machdep.h>
 #include <x86/mca.h>
-#include <machine/segments.h>
+#include <machine/pcb.h>
+#include <machine/pmap.h>
+#include <machine/specialreg.h>
+#include <machine/md_var.h>
+
+#ifdef SMP
+#include <x86/apicreg.h>
+#include <machine/smp.h>
+#include <machine/vmparam.h>
+#endif
 
 #include <contrib/dev/acpica/include/acpi.h>
 
@@ -57,164 +62,186 @@ __FBSDID("$FreeBSD$");
 #include "acpi_wakecode.h"
 #include "acpi_wakedata.h"
 
-/* Make sure the code is less than one page and leave room for the stack. */
+/* Make sure the code is less than a page and leave room for the stack. */
 CTASSERT(sizeof(wakecode) < PAGE_SIZE - 1024);
 
-#ifndef _SYS_CDEFS_H_
-#error this file needs sys/cdefs.h as a prerequisite
+extern int		acpi_resume_beep;
+extern int		acpi_reset_video;
+
+#ifdef SMP
+extern struct pcb	**susppcbs;
+#else
+static struct pcb	**susppcbs;
+#endif
+
+static void		*acpi_alloc_wakeup_handler(void);
+static void		acpi_stop_beep(void *);
+
+#ifdef SMP
+static int		acpi_wakeup_ap(struct acpi_softc *, int);
+static void		acpi_wakeup_cpus(struct acpi_softc *, const cpuset_t *);
 #endif
 
-extern uint32_t	acpi_resume_beep;
-extern uint32_t	acpi_reset_video;
-extern void	initializecpu(void);
-
-static struct region_descriptor __used	saved_idt, saved_gdt;
-static struct region_descriptor	*p_gdt;
-static uint16_t __used 	saved_ldt;
-
-static uint32_t	__used	r_eax, r_ebx, r_ecx, r_edx, r_ebp, r_esi, r_edi,
-			r_efl, r_cr0, r_cr2, r_cr3, r_cr4, ret_addr;
-
-static uint16_t	__used	r_cs, r_ds, r_es, r_fs, r_gs, r_ss, r_tr;
-static uint32_t	__used	r_esp;
-
-static void		acpi_printcpu(void);
-static void		acpi_realmodeinst(void *arg, bus_dma_segment_t *segs,
-					  int nsegs, int error);
-static void		acpi_alloc_wakeup_handler(void);
-
-/* XXX shut gcc up */
-extern int		acpi_savecpu(void);
-extern int		acpi_restorecpu(void);
-
-#ifdef __GNUCLIKE_ASM
-__asm__("				\n\
-	.text				\n\
-	.p2align 2, 0x90		\n\
-	.type acpi_restorecpu, @function\n\
-acpi_restorecpu:			\n\
-	.align 4			\n\
-	movl	r_eax,%eax		\n\
-	movl	r_ebx,%ebx		\n\
-	movl	r_ecx,%ecx		\n\
-	movl	r_edx,%edx		\n\
-	movl	r_ebp,%ebp		\n\
-	movl	r_esi,%esi		\n\
-	movl	r_edi,%edi		\n\
-	movl	r_esp,%esp		\n\
-					\n\
-	pushl	r_efl			\n\
-	popfl				\n\
-					\n\
-	movl	ret_addr,%eax		\n\
-	movl	%eax,(%esp)		\n\
-	xorl	%eax,%eax		\n\
-	ret				\n\
-					\n\
-	.text				\n\
-	.p2align 2, 0x90		\n\
-	.type acpi_savecpu, @function	\n\
-acpi_savecpu:				\n\
-	movw	%cs,r_cs		\n\
-	movw	%ds,r_ds		\n\
-	movw	%es,r_es		\n\
-	movw	%fs,r_fs		\n\
-	movw	%gs,r_gs		\n\
-	movw	%ss,r_ss		\n\
-					\n\
-	movl	%eax,r_eax		\n\
-	movl	%ebx,r_ebx		\n\
-	movl	%ecx,r_ecx		\n\
-	movl	%edx,r_edx		\n\
-	movl	%ebp,r_ebp		\n\
-	movl	%esi,r_esi		\n\
-	movl	%edi,r_edi		\n\
-					\n\
-	movl	%cr0,%eax		\n\
-	movl	%eax,r_cr0		\n\
-	movl	%cr2,%eax		\n\
-	movl	%eax,r_cr2		\n\
-	movl	%cr3,%eax		\n\
-	movl	%eax,r_cr3		\n\
-	movl	%cr4,%eax		\n\
-	movl	%eax,r_cr4		\n\
-					\n\
-	pushfl				\n\
-	popl	r_efl			\n\
-					\n\
-	movl	%esp,r_esp		\n\
-					\n\
-	sgdt	saved_gdt		\n\
-	sidt	saved_idt		\n\
-	sldt	saved_ldt		\n\
-	str	r_tr			\n\
-					\n\
-	movl	(%esp),%eax		\n\
-	movl	%eax,ret_addr		\n\
-	movl	$1,%eax			\n\
-	ret				\n\
-");
-#endif /* __GNUCLIKE_ASM */
+#define ACPI_PAGETABLES	0
+#define	WAKECODE_VADDR(sc)	((sc)->acpi_wakeaddr + (ACPI_PAGETABLES * PAGE_SIZE))
+#define	WAKECODE_PADDR(sc)	((sc)->acpi_wakephys + (ACPI_PAGETABLES * PAGE_SIZE))
+#define	WAKECODE_FIXUP(offset, type, val) do	{	\
+	type	*addr;					\
+	addr = (type *)(WAKECODE_VADDR(sc) + offset);	\
+	*addr = val;					\
+} while (0)
 
 static void
-acpi_printcpu(void)
+acpi_stop_beep(void *arg)
 {
-	printf("======== acpi_printcpu() debug dump ========\n");
-	printf("gdt[%04x:%08x] idt[%04x:%08x] ldt[%04x] tr[%04x] efl[%08x]\n",
-		saved_gdt.rd_limit, saved_gdt.rd_base,
-		saved_idt.rd_limit, saved_idt.rd_base,
-		saved_ldt, r_tr, r_efl);
-	printf("eax[%08x] ebx[%08x] ecx[%08x] edx[%08x]\n",
-		r_eax, r_ebx, r_ecx, r_edx);
-	printf("esi[%08x] edi[%08x] ebp[%08x] esp[%08x]\n",
-		r_esi, r_edi, r_ebp, r_esp);
-	printf("cr0[%08x] cr2[%08x] cr3[%08x] cr4[%08x]\n",
-		r_cr0, r_cr2, r_cr3, r_cr4);
-	printf("cs[%04x] ds[%04x] es[%04x] fs[%04x] gs[%04x] ss[%04x]\n",
-		r_cs, r_ds, r_es, r_fs, r_gs, r_ss);
+
+	if (acpi_resume_beep != 0)
+		timer_spkr_release();
 }
 
-#define WAKECODE_FIXUP(offset, type, val) do	{		\
-	type	*addr;						\
-	addr = (type *)(sc->acpi_wakeaddr + offset);		\
-	*addr = val;						\
-} while (0)
+#ifdef SMP
+static int
+acpi_wakeup_ap(struct acpi_softc *sc, int cpu)
+{
+	int		vector = (WAKECODE_PADDR(sc) >> 12) & 0xff;
+	int		apic_id = cpu_apic_ids[cpu];
+	int		ms;
+
+	WAKECODE_FIXUP(wakeup_pcb, struct pcb *, susppcbs[cpu]);
+
+	/* do an INIT IPI: assert RESET */
+	lapic_ipi_raw(APIC_DEST_DESTFLD | APIC_TRIGMOD_EDGE |
+	    APIC_LEVEL_ASSERT | APIC_DESTMODE_PHY | APIC_DELMODE_INIT, apic_id);
+
+	/* wait for pending status end */
+	lapic_ipi_wait(-1);
+
+	/* do an INIT IPI: deassert RESET */
+	lapic_ipi_raw(APIC_DEST_ALLESELF | APIC_TRIGMOD_LEVEL |
+	    APIC_LEVEL_DEASSERT | APIC_DESTMODE_PHY | APIC_DELMODE_INIT, 0);
+
+	/* wait for pending status end */
+	DELAY(10000);		/* wait ~10mS */
+	lapic_ipi_wait(-1);
 
-#define WAKECODE_BCOPY(offset, type, val) do	{		\
-	void	*addr;						\
-	addr = (void *)(sc->acpi_wakeaddr + offset);		\
-	bcopy(&(val), addr, sizeof(type));			\
-} while (0)
+	/*
+	 * next we do a STARTUP IPI: the previous INIT IPI might still be
+	 * latched, (P5 bug) this 1st STARTUP would then terminate
+	 * immediately, and the previously started INIT IPI would continue. OR
+	 * the previous INIT IPI has already run. and this STARTUP IPI will
+	 * run. OR the previous INIT IPI was ignored. and this STARTUP IPI
+	 * will run.
+	 */
+
+	/* do a STARTUP IPI */
+	lapic_ipi_raw(APIC_DEST_DESTFLD | APIC_TRIGMOD_EDGE |
+	    APIC_LEVEL_DEASSERT | APIC_DESTMODE_PHY | APIC_DELMODE_STARTUP |
+	    vector, apic_id);
+	lapic_ipi_wait(-1);
+	DELAY(200);		/* wait ~200uS */
+
+	/*
+	 * finally we do a 2nd STARTUP IPI: this 2nd STARTUP IPI should run IF
+	 * the previous STARTUP IPI was cancelled by a latched INIT IPI. OR
+	 * this STARTUP IPI will be ignored, as only ONE STARTUP IPI is
+	 * recognized after hardware RESET or INIT IPI.
+	 */
+
+	lapic_ipi_raw(APIC_DEST_DESTFLD | APIC_TRIGMOD_EDGE |
+	    APIC_LEVEL_DEASSERT | APIC_DESTMODE_PHY | APIC_DELMODE_STARTUP |
+	    vector, apic_id);
+	lapic_ipi_wait(-1);
+	DELAY(200);		/* wait ~200uS */
+
+	/* Wait up to 5 seconds for it to start. */
+	for (ms = 0; ms < 5000; ms++) {
+		if (susppcbs[cpu]->pcb_eip == 0)
+			return (1);	/* return SUCCESS */
+		DELAY(1000);
+	}
+	return (0);		/* return FAILURE */
+}
+
+#define	WARMBOOT_TARGET		0
+#define	WARMBOOT_OFF		(KERNBASE + 0x0467)
+#define	WARMBOOT_SEG		(KERNBASE + 0x0469)
+
+#define	CMOS_REG		(0x70)
+#define	CMOS_DATA		(0x71)
+#define	BIOS_RESET		(0x0f)
+#define	BIOS_WARM		(0x0a)
 
-/* Turn off bits 1&2 of the PIT, stopping the beep. */
 static void
-acpi_stop_beep(void *arg)
+acpi_wakeup_cpus(struct acpi_softc *sc, const cpuset_t *wakeup_cpus)
 {
-	outb(0x61, inb(0x61) & ~0x3);
+	uint32_t	mpbioswarmvec;
+	int		cpu;
+	u_char		mpbiosreason;
+
+	/* save the current value of the warm-start vector */
+	mpbioswarmvec = *((uint32_t *)WARMBOOT_OFF);
+	outb(CMOS_REG, BIOS_RESET);
+	mpbiosreason = inb(CMOS_DATA);
+
+	/* setup a vector to our boot code */
+	*((volatile u_short *)WARMBOOT_OFF) = WARMBOOT_TARGET;
+	*((volatile u_short *)WARMBOOT_SEG) = WAKECODE_PADDR(sc) >> 4;
+	outb(CMOS_REG, BIOS_RESET);
+	outb(CMOS_DATA, BIOS_WARM);	/* 'warm-start' */
+
+	/* Wake up each AP. */
+	for (cpu = 1; cpu < mp_ncpus; cpu++) {
+		if (!CPU_ISSET(cpu, wakeup_cpus))
+			continue;
+		if (acpi_wakeup_ap(sc, cpu) == 0) {
+			/* restore the warmstart vector */
+			*(uint32_t *)WARMBOOT_OFF = mpbioswarmvec;
+			panic("acpi_wakeup: failed to resume AP #%d (PHY #%d)",
+			    cpu, cpu_apic_ids[cpu]);
+		}
+	}
+
+	/* restore the warmstart vector */
+	*(uint32_t *)WARMBOOT_OFF = mpbioswarmvec;
+
+	outb(CMOS_REG, BIOS_RESET);
+	outb(CMOS_DATA, mpbiosreason);
 }
+#endif
 
 int
 acpi_sleep_machdep(struct acpi_softc *sc, int state)
 {
-	ACPI_STATUS		status;
-	struct pmap		*pm;
-	int			ret;
-	uint32_t		cr3;
-	u_long			ef;
+#ifdef SMP
+	cpuset_t	wakeup_cpus;
+#endif
+	register_t	cr3, rf;
+	ACPI_STATUS	status;
+	struct pmap	*pm;
+	int		ret;
 
 	ret = -1;
-	if (sc->acpi_wakeaddr == 0)
+
+	if (sc->acpi_wakeaddr == 0ul)
 		return (ret);
 
-	AcpiSetFirmwareWakingVector(sc->acpi_wakephys);
+#ifdef SMP
+	wakeup_cpus = all_cpus;
+	CPU_CLR(PCPU_GET(cpuid), &wakeup_cpus);
+#endif
+
+	if (acpi_resume_beep != 0)
+		timer_spkr_acquire();
 
-	ef = intr_disable();
+	AcpiSetFirmwareWakingVector(WAKECODE_PADDR(sc));
+
+	rf = intr_disable();
 	intr_suspend();
 
 	/*
-	 * Temporarily switch to the kernel pmap because it provides an
-	 * identity mapping (setup at boot) for the low physical memory
-	 * region containing the wakeup code.
+	 * Temporarily switch to the kernel pmap because it provides
+	 * an identity mapping (setup at boot) for the low physical
+	 * memory region containing the wakeup code.
 	 */
 	pm = kernel_pmap;
 	cr3 = rcr3();
@@ -224,39 +251,22 @@ acpi_sleep_machdep(struct acpi_softc *sc
 	load_cr3(vtophys(pm->pm_pdir));
 #endif
 
-	ret_addr = 0;
-	if (acpi_savecpu()) {
-		/* Execute Sleep */
-
-		p_gdt = (struct region_descriptor *)
-				(sc->acpi_wakeaddr + physical_gdt);
-		p_gdt->rd_limit = saved_gdt.rd_limit;
-		p_gdt->rd_base = vtophys(saved_gdt.rd_base);
-
-		WAKECODE_FIXUP(physical_esp, uint32_t, vtophys(r_esp));
-		WAKECODE_FIXUP(previous_cr0, uint32_t, r_cr0);
-		WAKECODE_FIXUP(previous_cr2, uint32_t, r_cr2);
-		WAKECODE_FIXUP(previous_cr3, uint32_t, r_cr3);
-		WAKECODE_FIXUP(previous_cr4, uint32_t, r_cr4);
-
-		WAKECODE_FIXUP(resume_beep, uint32_t, acpi_resume_beep);
-		WAKECODE_FIXUP(reset_video, uint32_t, acpi_reset_video);
-
-		WAKECODE_FIXUP(previous_tr,  uint16_t, r_tr);
-		WAKECODE_BCOPY(previous_gdt, struct region_descriptor, saved_gdt);
-		WAKECODE_FIXUP(previous_ldt, uint16_t, saved_ldt);
-		WAKECODE_BCOPY(previous_idt, struct region_descriptor, saved_idt);
-
-		WAKECODE_FIXUP(where_to_recover, void *, acpi_restorecpu);
-
-		WAKECODE_FIXUP(previous_ds,  uint16_t, r_ds);
-		WAKECODE_FIXUP(previous_es,  uint16_t, r_es);
-		WAKECODE_FIXUP(previous_fs,  uint16_t, r_fs);
-		WAKECODE_FIXUP(previous_gs,  uint16_t, r_gs);
-		WAKECODE_FIXUP(previous_ss,  uint16_t, r_ss);
+	if (suspendctx(susppcbs[0])) {
+#ifdef SMP
+		if (!CPU_EMPTY(&wakeup_cpus) &&
+		    suspend_cpus(wakeup_cpus) == 0) {
+			device_printf(sc->acpi_dev, "Failed to suspend APs\n");
+			goto out;
+		}
+#endif
+
+		WAKECODE_FIXUP(resume_beep, uint8_t, (acpi_resume_beep != 0));
+		WAKECODE_FIXUP(reset_video, uint8_t, (acpi_reset_video != 0));
+
+		WAKECODE_FIXUP(wakeup_cr4, register_t, susppcbs[0]->pcb_cr4);
+		WAKECODE_FIXUP(wakeup_cr3, register_t, susppcbs[0]->pcb_cr3);
 
-		if (bootverbose)
-			acpi_printcpu();
+		WAKECODE_FIXUP(wakeup_pcb, struct pcb *, susppcbs[0]);
 
 		/* Call ACPICA to enter the desired sleep state */
 		if (state == ACPI_STATE_S4 && sc->acpi_s4bios)
@@ -266,8 +276,8 @@ acpi_sleep_machdep(struct acpi_softc *sc
 
 		if (status != AE_OK) {
 			device_printf(sc->acpi_dev,
-				"AcpiEnterSleepState failed - %s\n",
-				AcpiFormatException(status));
+			    "AcpiEnterSleepState failed - %s\n",
+			    AcpiFormatException(status));
 			goto out;
 		}
 
@@ -275,97 +285,96 @@ acpi_sleep_machdep(struct acpi_softc *sc
 			ia32_pause();
 	} else {
 		pmap_init_pat();
+		initializecpu();
 		PCPU_SET(switchtime, 0);
 		PCPU_SET(switchticks, ticks);
-		if (bootverbose) {
-			acpi_savecpu();
-			acpi_printcpu();
-		}
+#ifdef SMP
+		if (!CPU_EMPTY(&wakeup_cpus))
+			acpi_wakeup_cpus(sc, &wakeup_cpus);
+#endif
 		ret = 0;
 	}
 
 out:
+#ifdef SMP
+	if (!CPU_EMPTY(&wakeup_cpus))
+		restart_cpus(wakeup_cpus);
+#endif
+
 	load_cr3(cr3);
 	mca_resume();
 	intr_resume();
-	intr_restore(ef);
+	intr_restore(rf);
+
+	AcpiSetFirmwareWakingVector(0);
 
 	if (ret == 0 && mem_range_softc.mr_op != NULL &&
 	    mem_range_softc.mr_op->reinit != NULL)
 		mem_range_softc.mr_op->reinit(&mem_range_softc);
 
-	/* If we beeped, turn it off after a delay. */
-	if (acpi_resume_beep)
-		timeout(acpi_stop_beep, NULL, 3 * hz);
-
 	return (ret);
 }
 
-static bus_dma_tag_t	acpi_waketag;
-static bus_dmamap_t	acpi_wakemap;
-static vm_offset_t	acpi_wakeaddr;
-
-static void
+static void *
 acpi_alloc_wakeup_handler(void)
 {
-	void *wakeaddr;
-
-	if (!cold)
-		return;
+	void		*wakeaddr;
+	int		i;
 
 	/*
 	 * Specify the region for our wakeup code.  We want it in the low 1 MB
-	 * region, excluding video memory and above (0xa0000).  We ask for
-	 * it to be page-aligned, just to be safe.
+	 * region, excluding real mode IVT (0-0x3ff), BDA (0x400-0x4ff), EBDA
+	 * (less than 128KB, below 0xa0000, must be excluded by SMAP and DSDT),
+	 * and ROM area (0xa0000 and above).  The temporary page tables must be
+	 * page-aligned.
 	 */
-	if (bus_dma_tag_create(/*parent*/ NULL,
-	    /*alignment*/ PAGE_SIZE, /*no boundary*/ 0,
-	    /*lowaddr*/ 0x9ffff, /*highaddr*/ BUS_SPACE_MAXADDR, NULL, NULL,
-	    /*maxsize*/ PAGE_SIZE, /*segments*/ 1, /*maxsegsize*/ PAGE_SIZE,
-	    0, busdma_lock_mutex, &Giant, &acpi_waketag) != 0) {
-		printf("acpi_alloc_wakeup_handler: can't create wake tag\n");
-		return;
+	wakeaddr = contigmalloc((ACPI_PAGETABLES + 1) * PAGE_SIZE, M_DEVBUF,
+	    M_NOWAIT, 0x500, 0xa0000, PAGE_SIZE, 0ul);
+	if (wakeaddr == NULL) {
+		printf("%s: can't alloc wake memory\n", __func__);
+		return (NULL);
 	}
-	if (bus_dmamem_alloc(acpi_waketag, &wakeaddr, BUS_DMA_NOWAIT,
-	    &acpi_wakemap) != 0) {
-		printf("acpi_alloc_wakeup_handler: can't alloc wake memory\n");
-		return;
+	if (EVENTHANDLER_REGISTER(power_resume, acpi_stop_beep, NULL,
+	    EVENTHANDLER_PRI_LAST) == NULL) {
+		printf("%s: can't register event handler\n", __func__);
+		contigfree(wakeaddr, (ACPI_PAGETABLES + 1) * PAGE_SIZE, M_DEVBUF);
+		return (NULL);
 	}
-	acpi_wakeaddr = (vm_offset_t)wakeaddr;
-}
-
-SYSINIT(acpiwakeup, SI_SUB_KMEM, SI_ORDER_ANY, acpi_alloc_wakeup_handler, 0);
-
-static void
-acpi_realmodeinst(void *arg, bus_dma_segment_t *segs, int nsegs, int error)
-{
-	struct acpi_softc *sc;
-	uint32_t *addr;
-
-	/* Overwrite the ljmp target with the real address */
-	sc = arg;
-	sc->acpi_wakephys = segs[0].ds_addr;
-	addr = (uint32_t *)&wakecode[wakeup_sw32 + 2];
-	*addr = sc->acpi_wakephys + wakeup_32;
-
-	/* Copy the wake code into our low page and save its physical addr. */
-	bcopy(wakecode, (void *)sc->acpi_wakeaddr, sizeof(wakecode));
-	if (bootverbose) {
-		device_printf(sc->acpi_dev, "wakeup code va %#x pa %#jx\n",
-		    acpi_wakeaddr, (uintmax_t)sc->acpi_wakephys);
+	susppcbs = malloc(mp_ncpus * sizeof(*susppcbs), M_DEVBUF, M_WAITOK);
+	for (i = 0; i < mp_ncpus; i++) {
+		susppcbs[i] = malloc(sizeof(**susppcbs), M_DEVBUF, M_WAITOK);
 	}
+
+	return (wakeaddr);
 }
 
 void
 acpi_install_wakeup_handler(struct acpi_softc *sc)
 {
-	if (acpi_wakeaddr == 0)
+	static void	*wakeaddr = NULL;
+
+	if (wakeaddr != NULL)
 		return;
 
-	sc->acpi_waketag = acpi_waketag;
-	sc->acpi_wakeaddr = acpi_wakeaddr;
-	sc->acpi_wakemap = acpi_wakemap;
+	wakeaddr = acpi_alloc_wakeup_handler();
+	if (wakeaddr == NULL)
+		return;
+
+	sc->acpi_wakeaddr = (vm_offset_t)wakeaddr;
+	sc->acpi_wakephys = vtophys(wakeaddr);
+
+	bcopy(wakecode, (void *)WAKECODE_VADDR(sc), sizeof(wakecode));
 
-	bus_dmamap_load(sc->acpi_waketag, sc->acpi_wakemap,
-	    (void *)sc->acpi_wakeaddr, PAGE_SIZE, acpi_realmodeinst, sc, 0);
+	/* Patch GDT base address, ljmp target. */
+	WAKECODE_FIXUP((bootgdtdesc + 2), uint32_t,
+	    WAKECODE_PADDR(sc) + bootgdt);
+	WAKECODE_FIXUP((wakeup_sw32 + 2), uint32_t,
+	    WAKECODE_PADDR(sc) + wakeup_32);
+
+	/* Save pointers to some global data. */
+	WAKECODE_FIXUP(wakeup_ret, void *, resumectx);
+
+	if (bootverbose)
+		device_printf(sc->acpi_dev, "wakeup code va %p pa %p\n",
+		    (void *)sc->acpi_wakeaddr, (void *)sc->acpi_wakephys);
 }

Modified: head/sys/i386/i386/apic_vector.s
==============================================================================
--- head/sys/i386/i386/apic_vector.s	Fri May 18 18:53:28 2012	(r235621)
+++ head/sys/i386/i386/apic_vector.s	Fri May 18 18:55:58 2012	(r235622)
@@ -334,6 +334,24 @@ IDTVEC(cpustop)
 	iret
 
 /*
+ * Executed by a CPU when it receives an IPI_SUSPEND from another CPU.
+ */
+	.text
+	SUPERALIGN_TEXT
+IDTVEC(cpususpend)
+	PUSH_FRAME
+	SET_KERNEL_SREGS
+	cld
+
+	movl	lapic, %eax
+	movl	$0, LA_EOI(%eax)	/* End Of Interrupt to APIC */
+
+	call	cpususpend_handler
+
+	POP_FRAME
+	jmp	doreti_iret
+
+/*
  * Executed by a CPU when it receives a RENDEZVOUS IPI from another CPU.
  *
  * - Calls the generic rendezvous action function.

Modified: head/sys/i386/i386/genassym.c
==============================================================================
--- head/sys/i386/i386/genassym.c	Fri May 18 18:53:28 2012	(r235621)
+++ head/sys/i386/i386/genassym.c	Fri May 18 18:55:58 2012	(r235622)
@@ -121,7 +121,10 @@ ASSYM(VM_MAXUSER_ADDRESS, VM_MAXUSER_ADD
 ASSYM(KERNBASE, KERNBASE);
 ASSYM(KERNLOAD, KERNLOAD);
 ASSYM(MCLBYTES, MCLBYTES);

*** DIFF OUTPUT TRUNCATED AT 1000 LINES ***



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