Author: jkim
Date: Tue Mar 17 00:48:11 2009
New Revision: 189903
URL: http://svn.freebsd.org/changeset/base/189903

Log:
  Initial suspend/resume support for amd64.
  
  This code is heavily inspired by Takanori Watanabe's experimental SMP patch
  for i386 and large portion was shamelessly cut and pasted from Peter Wemm's
  AP boot code.

Added:
  head/sys/amd64/acpica/Makefile   (contents, props changed)
  head/sys/amd64/acpica/acpi_switch.S   (contents, props changed)
  head/sys/amd64/acpica/acpi_wakecode.S   (contents, props changed)
  head/sys/amd64/acpica/genwakecode.sh   (contents, props changed)
  head/sys/amd64/acpica/genwakedata.sh   (contents, props changed)
Modified:
  head/sys/amd64/acpica/acpi_machdep.c
  head/sys/amd64/acpica/acpi_wakeup.c
  head/sys/amd64/amd64/amd64_mem.c
  head/sys/amd64/amd64/apic_vector.S
  head/sys/amd64/amd64/cpu_switch.S
  head/sys/amd64/amd64/db_trace.c
  head/sys/amd64/amd64/genassym.c
  head/sys/amd64/amd64/mp_machdep.c
  head/sys/amd64/include/apicvar.h
  head/sys/amd64/include/pcb.h
  head/sys/amd64/include/smp.h
  head/sys/conf/files.amd64
  head/sys/dev/acpica/acpi.c
  head/sys/dev/acpica/acpi_ec.c
  head/sys/i386/i386/i686_mem.c
  head/sys/i386/i386/k6_mem.c
  head/sys/kern/subr_smp.c
  head/sys/sys/memrange.h
  head/sys/sys/smp.h

Added: head/sys/amd64/acpica/Makefile
==============================================================================
--- /dev/null   00:00:00 1970   (empty, because file is newly added)
+++ head/sys/amd64/acpica/Makefile      Tue Mar 17 00:48:11 2009        
(r189903)
@@ -0,0 +1,33 @@
+# $FreeBSD$
+
+# Correct path for kernel builds
+# Don't rely on the kernel's .depend file
+.ifdef MAKESRCPATH
+.PATH: ${MAKESRCPATH}
+DEPENDFILE=
+.else
+MAKESRCPATH= ${.CURDIR}
+CLEANFILES= acpi_wakecode.h acpi_wakedata.h acpi_wakecode.bin acpi_wakecode.o
+.endif
+.if ${CC} == "icc"
+CFLAGS+=       -restrict
+NOSTDINC=      -X
+.else
+NOSTDINC=      -nostdinc
+.endif
+CFLAGS+=       ${NOSTDINC} -include opt_global.h -I. -I${MAKESRCPATH}/../..
+
+all: acpi_wakecode.h acpi_wakedata.h
+
+acpi_wakecode.o: acpi_wakecode.S assym.s
+
+acpi_wakecode.bin: acpi_wakecode.o
+       objcopy -S -O binary acpi_wakecode.o acpi_wakecode.bin
+
+acpi_wakecode.h: acpi_wakecode.bin
+       sh ${MAKESRCPATH}/genwakecode.sh > acpi_wakecode.h
+
+acpi_wakedata.h: acpi_wakecode.bin
+       sh ${MAKESRCPATH}/genwakedata.sh > acpi_wakedata.h
+
+.include <bsd.prog.mk>

Modified: head/sys/amd64/acpica/acpi_machdep.c
==============================================================================
--- head/sys/amd64/acpica/acpi_machdep.c        Mon Mar 16 23:56:28 2009        
(r189902)
+++ head/sys/amd64/acpica/acpi_machdep.c        Tue Mar 17 00:48:11 2009        
(r189903)
@@ -31,25 +31,50 @@ __FBSDID("$FreeBSD$");
 #include <sys/bus.h>
 #include <sys/kernel.h>
 #include <sys/module.h>
+#include <sys/sysctl.h>
 
 #include <contrib/dev/acpica/acpi.h>
 #include <dev/acpica/acpivar.h>
 
 #include <machine/nexusvar.h>
 
+SYSCTL_DECL(_debug_acpi);
+
+uint32_t acpi_resume_beep;
+TUNABLE_INT("debug.acpi.resume_beep", &acpi_resume_beep);
+SYSCTL_UINT(_debug_acpi, OID_AUTO, resume_beep, CTLFLAG_RW, &acpi_resume_beep,
+    0, "Beep the PC speaker when resuming");
+uint32_t acpi_reset_video;
+TUNABLE_INT("hw.acpi.reset_video", &acpi_reset_video);
+
 static int intr_model = ACPI_INTR_PIC;
+static struct apm_clone_data acpi_clone;
 
 int
 acpi_machdep_init(device_t dev)
 {
-       struct  acpi_softc *sc;
+       struct acpi_softc       *sc;
 
        sc = devclass_get_softc(devclass_find("acpi"), 0);
+
+       /* Create a fake clone for /dev/acpi. */
+       STAILQ_INIT(&sc->apm_cdevs);
+       acpi_clone.cdev = sc->acpi_dev_t;
+       acpi_clone.acpi_sc = sc;
+       ACPI_LOCK(acpi);
+       STAILQ_INSERT_TAIL(&sc->apm_cdevs, &acpi_clone, entries);
+       ACPI_UNLOCK(acpi);
+       sc->acpi_clone = &acpi_clone;
        acpi_install_wakeup_handler(sc);
 
        if (intr_model != ACPI_INTR_PIC)
                acpi_SetIntrModel(intr_model);
 
+       SYSCTL_ADD_UINT(&sc->acpi_sysctl_ctx,
+           SYSCTL_CHILDREN(sc->acpi_sysctl_tree), OID_AUTO,
+           "reset_video", CTLFLAG_RW, &acpi_reset_video, 0,
+           "Call the VESA reset BIOS vector on the resume path");
+
        return (0);
 }
 

Added: head/sys/amd64/acpica/acpi_switch.S
==============================================================================
--- /dev/null   00:00:00 1970   (empty, because file is newly added)
+++ head/sys/amd64/acpica/acpi_switch.S Tue Mar 17 00:48:11 2009        
(r189903)
@@ -0,0 +1,177 @@
+/*-
+ * Copyright (c) 2001 Takanori Watanabe <takaw...@jp.freebsd.org>
+ * Copyright (c) 2001 Mitsuru IWASAKI <iwas...@jp.freebsd.org>
+ * Copyright (c) 2008-2009 Jung-uk Kim <j...@freebsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <machine/asmacros.h>
+#include <machine/specialreg.h>
+
+#include "acpi_wakedata.h"
+#include "assym.s"
+
+#define        WAKEUP_DECL(member)     \
+    .set WAKEUP_ ## member, wakeup_ ## member - wakeup_ctx
+
+       WAKEUP_DECL(xpcb)
+       WAKEUP_DECL(gdt)
+       WAKEUP_DECL(efer)
+       WAKEUP_DECL(pat)
+       WAKEUP_DECL(star)
+       WAKEUP_DECL(lstar)
+       WAKEUP_DECL(cstar)
+       WAKEUP_DECL(sfmask)
+       WAKEUP_DECL(cpu)
+
+#define        WAKEUP_CTX(member)      WAKEUP_ ## member (%rdi)
+#define        WAKEUP_PCB(member)      PCB_ ## member(%r11)
+#define        WAKEUP_XPCB(member)     XPCB_ ## member(%r11)
+
+ENTRY(acpi_restorecpu)
+       /* Switch to KPML4phys. */
+       movq    %rsi, %rax
+       movq    %rax, %cr3
+
+       /* Restore GDT. */
+       lgdt    WAKEUP_CTX(gdt)
+       jmp     1f
+1:
+
+       /* Fetch PCB. */
+       movq    WAKEUP_CTX(xpcb), %r11
+
+       /* Restore segment registers. */
+       mov     WAKEUP_PCB(DS), %ds
+       mov     WAKEUP_PCB(ES), %es
+       mov     WAKEUP_XPCB(SS), %ss
+       mov     WAKEUP_PCB(FS), %fs
+       mov     WAKEUP_PCB(GS), %gs
+
+       movl    $MSR_FSBASE, %ecx
+       movl    WAKEUP_PCB(FSBASE), %eax
+       movl    4 + WAKEUP_PCB(FSBASE), %edx
+       wrmsr
+       movl    $MSR_GSBASE, %ecx
+       movl    WAKEUP_PCB(GSBASE), %eax
+       movl    4 + WAKEUP_PCB(GSBASE), %edx
+       wrmsr
+       movl    $MSR_KGSBASE, %ecx
+       movl    WAKEUP_XPCB(KGSBASE), %eax
+       movl    4 + WAKEUP_XPCB(KGSBASE), %edx
+       wrmsr
+
+       /* Restore EFER. */
+       movl    $MSR_EFER, %ecx
+       movl    WAKEUP_CTX(efer), %eax
+       wrmsr
+
+       /* Restore PAT. */
+       movl    $MSR_PAT, %ecx
+       movl    WAKEUP_CTX(pat), %eax
+       movl    4 + WAKEUP_CTX(pat), %edx
+       wrmsr
+
+       /* Restore fast syscall stuff. */
+       movl    $MSR_STAR, %ecx
+       movl    WAKEUP_CTX(star), %eax
+       movl    4 + WAKEUP_CTX(star), %edx
+       wrmsr
+       movl    $MSR_LSTAR, %ecx
+       movl    WAKEUP_CTX(lstar), %eax
+       movl    4 + WAKEUP_CTX(lstar), %edx
+       wrmsr
+       movl    $MSR_CSTAR, %ecx
+       movl    WAKEUP_CTX(cstar), %eax
+       movl    4 + WAKEUP_CTX(cstar), %edx
+       wrmsr
+       movl    $MSR_SF_MASK, %ecx
+       movl    WAKEUP_CTX(sfmask), %eax
+       wrmsr
+
+       /* Restore CR0, CR2 and CR4. */
+       movq    WAKEUP_XPCB(CR0), %rax
+       movq    %rax, %cr0
+       movq    WAKEUP_XPCB(CR2), %rax
+       movq    %rax, %cr2
+       movq    WAKEUP_XPCB(CR4), %rax
+       movq    %rax, %cr4
+
+       /* Restore descriptor tables. */
+       lidt    WAKEUP_XPCB(IDT)
+       lldt    WAKEUP_XPCB(LDT)
+       movw    WAKEUP_XPCB(TR), %ax
+       ltr     %ax
+
+       /* Restore other callee saved registers. */
+       movq    WAKEUP_PCB(R15), %r15
+       movq    WAKEUP_PCB(R14), %r14
+       movq    WAKEUP_PCB(R13), %r13
+       movq    WAKEUP_PCB(R12), %r12
+       movq    WAKEUP_PCB(RBP), %rbp
+       movq    WAKEUP_PCB(RSP), %rsp
+       movq    WAKEUP_PCB(RBX), %rbx
+
+       /* Restore debug registers. */
+       movq    WAKEUP_PCB(DR0), %rax
+       movq    %rax, %dr0
+       movq    WAKEUP_PCB(DR1), %rax
+       movq    %rax, %dr1
+       movq    WAKEUP_PCB(DR2), %rax
+       movq    %rax, %dr2
+       movq    WAKEUP_PCB(DR3), %rax
+       movq    %rax, %dr3
+       movq    WAKEUP_PCB(DR6), %rax
+       movq    %rax, %dr6
+       movq    WAKEUP_PCB(DR7), %rax
+       movq    %rax, %dr7
+
+       /* Restore return address. */
+       movq    WAKEUP_PCB(RIP), %rax
+       movq    %rax, (%rsp)
+
+       /* Indicate the CPU is resumed. */
+       xorl    %eax, %eax
+       movl    %eax, WAKEUP_CTX(cpu)
+
+       ret
+END(acpi_restorecpu)
+
+ENTRY(acpi_savecpu)
+       /* Fetch XPCB and save CPU context. */
+       movq    %rdi, %r10
+       call    savectx2
+       movq    %r10, %r11
+
+       /* Patch caller's return address and stack pointer. */
+       movq    (%rsp), %rax
+       movq    %rax, WAKEUP_PCB(RIP)
+       movq    %rsp, %rax
+       movq    %rax, WAKEUP_PCB(RSP)
+
+       movl    $1, %eax
+       ret
+END(acpi_savecpu)

Added: head/sys/amd64/acpica/acpi_wakecode.S
==============================================================================
--- /dev/null   00:00:00 1970   (empty, because file is newly added)
+++ head/sys/amd64/acpica/acpi_wakecode.S       Tue Mar 17 00:48:11 2009        
(r189903)
@@ -0,0 +1,278 @@
+/*-
+ * Copyright (c) 2001 Takanori Watanabe <takaw...@jp.freebsd.org>
+ * Copyright (c) 2001 Mitsuru IWASAKI <iwas...@jp.freebsd.org>
+ * Copyright (c) 2003 Peter Wemm
+ * Copyright (c) 2008 Jung-uk Kim <j...@freebsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#define LOCORE
+
+#include <machine/asmacros.h>
+#include <machine/specialreg.h>
+
+#include "assym.s"
+
+/*
+ * Resume entry point for real mode.
+ *
+ * If XFirmwareWakingVector is zero and FirmwareWakingVector is non-zero
+ * in FACS, the BIOS enters here in real mode after POST with CS set to
+ * (FirmwareWakingVector >> 4) and IP set to (FirmwareWakingVector & 0xf).
+ * 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).
+ *
+ * Note: If XFirmwareWakingVector is non-zero, it should disable address
+ * translation/paging and interrupts, load all segment registers with
+ * a flat 4 GB address space, and set EFLAGS.IF to zero.  Currently
+ * this mode is not supported by this code.
+ */
+
+       .data                           /* So we can modify it */
+
+       ALIGN_TEXT
+wakeup_start:
+       .code16
+       /*
+        * Set up segment registers for real mode, a small stack for
+        * any calls we make, and clear any flags.
+        */
+       cli                             /* make sure no interrupts */
+       cld
+       mov     %cs, %ax                /* copy %cs to %ds.  Remember these */
+       mov     %ax, %ds                /* are offsets rather than selectors */
+       mov     %ax, %ss
+       movw    $PAGE_SIZE - 8, %sp
+       pushw   $0
+       popfw
+
+       /* To debug resume hangs, beep the speaker if the user requested. */
+       cmpw    $0, resume_beep - wakeup_start
+       je      1f
+       movb    $0xc0, %al
+       outb    %al, $0x42
+       movb    $0x04, %al
+       outb    %al, $0x42
+       inb     $0x61, %al
+       orb     $0x3, %al
+       outb    %al, $0x61
+       movw    $0, resume_beep - wakeup_start
+1:
+
+       /* Re-initialize video BIOS if the reset_video tunable is set. */
+       cmpw    $0, reset_video - wakeup_start
+       je      1f
+       lcall   $0xc000, $3
+       movw    $0, reset_video - wakeup_start
+
+       /*
+        * Set up segment registers for real mode again in case the
+        * previous BIOS call clobbers them.
+        */
+       mov     %cs, %ax
+       mov     %ax, %ds
+       mov     %ax, %ss
+1:
+
+       /*
+        * Find relocation base and patch the gdt descript and ljmp targets
+        */
+       xorl    %ebx, %ebx
+       mov     %cs, %bx
+       sall    $4, %ebx                /* %ebx is now our relocation base */
+
+       /*
+        * 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_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:
+       .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 */
+
+       /*
+        * At this point, we are running in 32 bit legacy protected mode.
+        */
+       .code32
+wakeup_32:
+
+       mov     $bootdata32 - bootgdt, %eax
+       mov     %ax, %ds
+
+       /* Turn on the PAE and PSE bits for when paging is enabled */
+       mov     %cr4, %eax
+       orl     $(CR4_PAE | CR4_PSE), %eax
+       mov     %eax, %cr4
+
+       /*
+        * Enable EFER.LME so that we get long mode when all the prereqs are
+        * in place.  In this case, it turns on when CR0_PG is finally enabled.
+        * Pick up a few other EFER bits that we'll use need we're here.
+        */
+       movl    $MSR_EFER, %ecx
+       rdmsr
+       orl     $EFER_LME | EFER_SCE, %eax
+       wrmsr
+
+       /*
+        * Point to the embedded page tables for startup.  Note that this
+        * only gets accessed after we're actually in 64 bit mode, however
+        * we can only set the bottom 32 bits of %cr3 in this state.  This
+        * means we are required to use a temporary page table that is below
+        * the 4GB limit.  %ebx is still our relocation base.  We could just
+        * subtract 3 * PAGE_SIZE, but that would be too easy.
+        */
+       leal    wakeup_pagetables - wakeup_start(%ebx), %eax
+       movl    (%eax), %eax
+       mov     %eax, %cr3
+
+       /*
+        * 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.
+        */
+       mov     %cr0, %eax
+       orl     $CR0_PG, %eax
+       mov     %eax, %cr0
+
+       /*
+        * At this point paging is enabled, and we are in "compatability" mode.
+        * We do another far jump to reload %cs with the 64 bit selector.
+        * %cr3 points to a 4-level page table page.
+        * We cannot yet jump all the way to the kernel because we can only
+        * specify a 32 bit linear address.  So, yet another trampoline.
+        *
+        * The following instruction is:
+        * ljmp $bootcode64 - bootgdt, $wakeup_64 - 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_sw64:
+       .byte   0xea                    /* opcode for far jump */
+       .long   wakeup_64 - wakeup_start /* offset in segment */
+       .word   bootcode64 - bootgdt    /* index in gdt for 64 bit code */
+
+       /*
+        * Yeehar!  We're running in 64-bit mode!  We can mostly ignore our
+        * segment registers, and get on with it.
+        * Note that we are running at the correct virtual address, but with
+        * a 1:1 1GB mirrored mapping over entire address space.  We had better
+        * switch to a real %cr3 promptly so that we can get to the direct map
+        * space. Remember that jmp is relative and that we've been relocated,
+        * so use an indirect jump.
+        */
+       .code64
+wakeup_64:
+       mov     $bootdata64 - bootgdt, %eax
+       mov     %ax, %ds
+
+       /* Restore arguments and return. */
+       movq    wakeup_ctx - wakeup_start(%rbx), %rdi
+       movq    wakeup_kpml4 - wakeup_start(%rbx), %rsi
+       movq    wakeup_retaddr - wakeup_start(%rbx), %rax
+       jmp     *%rax
+
+       ALIGN_DATA
+bootgdt:
+       .long   0x00000000
+       .long   0x00000000
+
+bootcode64:
+       .long   0x0000ffff
+       .long   0x00af9b00
+
+bootdata64:
+       .long   0x0000ffff
+       .long   0x00af9300
+
+bootcode32:
+       .long   0x0000ffff
+       .long   0x00cf9b00
+
+bootdata32:
+       .long   0x0000ffff
+       .long   0x00cf9300
+bootgdtend:
+
+wakeup_pagetables:
+       .long   0
+
+bootgdtdesc:
+       .word   bootgdtend - bootgdt    /* Length */
+       .long   bootgdt - wakeup_start  /* Offset plus %ds << 4 */
+
+       ALIGN_DATA
+resume_beep:
+       .long   0
+reset_video:
+       .long   0
+wakeup_retaddr:
+       .quad   0
+wakeup_kpml4:
+       .quad   0
+
+wakeup_ctx:
+       .quad   0
+wakeup_xpcb:
+       .quad   0
+wakeup_gdt:
+       .word   0
+       .quad   0
+wakeup_efer:
+       .quad   0
+wakeup_pat:
+       .quad   0
+wakeup_star:
+       .quad   0
+wakeup_lstar:
+       .quad   0
+wakeup_cstar:
+       .quad   0
+wakeup_sfmask:
+       .quad   0
+wakeup_cpu:
+       .long   0
+dummy:

Modified: head/sys/amd64/acpica/acpi_wakeup.c
==============================================================================
--- head/sys/amd64/acpica/acpi_wakeup.c Mon Mar 16 23:56:28 2009        
(r189902)
+++ head/sys/amd64/acpica/acpi_wakeup.c Tue Mar 17 00:48:11 2009        
(r189903)
@@ -1,6 +1,8 @@
 /*-
  * Copyright (c) 2001 Takanori Watanabe <takaw...@jp.freebsd.org>
  * Copyright (c) 2001 Mitsuru IWASAKI <iwas...@jp.freebsd.org>
+ * Copyright (c) 2003 Peter Wemm
+ * Copyright (c) 2008-2009 Jung-uk Kim <j...@freebsd.org>
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -29,18 +31,411 @@
 __FBSDID("$FreeBSD$");
 
 #include <sys/param.h>
+#include <sys/systm.h>
 #include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/memrange.h>
+#include <sys/smp.h>
+#include <sys/sysctl.h>
+#include <sys/types.h>
+
+#include <vm/vm.h>
+#include <vm/pmap.h>
+
+#include <machine/intr_machdep.h>
+#include <machine/pcb.h>
+#include <machine/pmap.h>
+#include <machine/specialreg.h>
+#include <machine/vmparam.h>
+
+#ifdef SMP
+#include <machine/apicreg.h>
+#include <machine/smp.h>
+#endif
 
 #include <contrib/dev/acpica/acpi.h>
 #include <dev/acpica/acpivar.h>
 
+#include "acpi_wakecode.h"
+#include "acpi_wakedata.h"
+
+/* 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
+#endif
+
+extern uint32_t                acpi_resume_beep;
+extern uint32_t                acpi_reset_video;
+
+#ifdef SMP
+extern struct xpcb     *stopxpcbs;
+#else
+static struct xpcb     *stopxpcbs;
+#endif
+
+int                    acpi_restorecpu(struct xpcb *, vm_offset_t);
+int                    acpi_savecpu(struct xpcb *);
+
+static void            acpi_reset_tss(int cpu);
+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 *, cpumask_t);
+#endif
+
+#define        WAKECODE_VADDR(sc)      ((sc)->acpi_wakeaddr + (3 * PAGE_SIZE))
+#define        WAKECODE_PADDR(sc)      ((sc)->acpi_wakephys + (3 * PAGE_SIZE))
+#define        WAKECODE_FIXUP(offset, type, val) do    {       \
+       type    *addr;                                  \
+       addr = (type *)(WAKECODE_VADDR(sc) + offset);   \
+       *addr = val;                                    \
+} while (0)
+
+/* Turn off bits 1&2 of the PIT, stopping the beep. */
+static void
+acpi_stop_beep(void *arg)
+{
+       outb(0x61, inb(0x61) & ~0x3);
+}
+
+#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_xpcb, struct xpcb *, &stopxpcbs[cpu]);
+       WAKECODE_FIXUP(wakeup_gdt, uint16_t, stopxpcbs[cpu].xpcb_gdt.rd_limit);
+       WAKECODE_FIXUP(wakeup_gdt + 2, uint64_t,
+           stopxpcbs[cpu].xpcb_gdt.rd_base);
+       WAKECODE_FIXUP(wakeup_cpu, int, cpu);
+
+       acpi_reset_tss(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);
+
+       /*
+        * 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 (*(int *)(WAKECODE_VADDR(sc) + wakeup_cpu) == 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)
+
+static void
+acpi_wakeup_cpus(struct acpi_softc *sc, cpumask_t wakeup_cpus)
+{
+       uint32_t        mpbioswarmvec;
+       cpumask_t       map;
+       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++) {
+               map = 1ul << cpu;
+               if ((wakeup_cpus & map) != map)
+                       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
+
+static void
+acpi_reset_tss(int cpu)
+{
+       uint32_t        *tss;
+
+       /*
+        * We have to clear "task busy" bit in TSS to restore
+        * task register later.  Otherwise, ltr causes GPF.
+        */
+       tss = (uint32_t *)&gdt[NGDT * cpu + GPROC0_SEL] + 1;
+       *tss &= ~((SDT_SYSBSY ^ SDT_SYSTSS) << 8);
+}
+
 int
 acpi_sleep_machdep(struct acpi_softc *sc, int state)
 {
-       return (0);
+       struct savefpu  *stopfpu;
+#ifdef SMP
+       cpumask_t       wakeup_cpus;
+#endif
+       register_t      cr3, rf;
+       ACPI_STATUS     status;
+       int             ret;
+
+       ret = -1;
+
+       if (sc->acpi_wakeaddr == 0ul)
+               return (ret);
+
+#ifdef SMP
+       wakeup_cpus = PCPU_GET(other_cpus);
+#endif
+
+       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.
+        */
+       cr3 = rcr3();
+       load_cr3(KPML4phys);
+
+       stopfpu = &stopxpcbs[0].xpcb_pcb.pcb_save;
+       if (acpi_savecpu(&stopxpcbs[0])) {
+               fpugetregs(curthread, stopfpu);
+
+#ifdef SMP
+               if (wakeup_cpus != 0 && suspend_cpus(wakeup_cpus) == 0) {
+                       device_printf(sc->acpi_dev,
+                           "Failed to suspend APs: CPU mask = 0x%jx\n",
+                           (uintmax_t)(wakeup_cpus & ~stopped_cpus));
+                       goto out;
+               }
+#endif
+
+               WAKECODE_FIXUP(resume_beep, uint32_t, acpi_resume_beep);
+               WAKECODE_FIXUP(reset_video, uint32_t, acpi_reset_video);
+
+               WAKECODE_FIXUP(wakeup_xpcb, struct xpcb *, &stopxpcbs[0]);
+               WAKECODE_FIXUP(wakeup_gdt, uint16_t,
+                   stopxpcbs[0].xpcb_gdt.rd_limit);
+               WAKECODE_FIXUP(wakeup_gdt + 2, uint64_t,
+                   stopxpcbs[0].xpcb_gdt.rd_base);
+               WAKECODE_FIXUP(wakeup_cpu, int, 0);
+
+               acpi_reset_tss(0);
+
+               /* Call ACPICA to enter the desired sleep state */
+               if (state == ACPI_STATE_S4 && sc->acpi_s4bios)
+                       status = AcpiEnterSleepStateS4bios();
+               else
+                       status = AcpiEnterSleepState(state);
+
+               if (status != AE_OK) {
+                       device_printf(sc->acpi_dev,
+                           "AcpiEnterSleepState failed - %s\n",
+                           AcpiFormatException(status));
+                       goto out;
+               }
+
+               for (;;)
+                       ia32_pause();
+       } else {
+               fpusetregs(curthread, stopfpu);
+
+               WAKECODE_FIXUP(resume_beep, uint32_t, 0);
+               WAKECODE_FIXUP(reset_video, uint32_t, 0);
+#ifdef SMP
+               if (wakeup_cpus != 0)
+                       acpi_wakeup_cpus(sc, wakeup_cpus);
+#endif
+               ret = 0;
+       }
+
+out:
+#ifdef SMP
+       if (wakeup_cpus != 0)
+               restart_cpus(wakeup_cpus);
+#endif
+
+       load_cr3(cr3);
+       intr_resume();
+       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 vm_offset_t     acpi_wakeaddr;
+
+static void
+acpi_alloc_wakeup_handler(void)
+{
+       void            *wakeaddr;
+
+       if (!cold)
+               return;
+
+       /*
+        * 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.
+        */
+       wakeaddr = contigmalloc(4 * PAGE_SIZE, M_DEVBUF, M_NOWAIT, 0, 0x9ffff,
+           PAGE_SIZE, 0ul);
+       if (wakeaddr == NULL) {
+               printf("%s: can't alloc wake memory\n", __func__);
+               return;
+       }
+       stopxpcbs = malloc(mp_ncpus * sizeof(*stopxpcbs), M_DEVBUF, M_NOWAIT);
+       if (stopxpcbs == NULL) {
+               contigfree(wakeaddr, 4 * PAGE_SIZE, M_DEVBUF);
+               printf("%s: can't alloc CPU state memory\n", __func__);
+               return;
+       }
+       acpi_wakeaddr = (vm_offset_t)wakeaddr;
 }
 
+SYSINIT(acpiwakeup, SI_SUB_KMEM, SI_ORDER_ANY, acpi_alloc_wakeup_handler, 0);
+
 void
 acpi_install_wakeup_handler(struct acpi_softc *sc)
 {
+       uint64_t        *pt4, *pt3, *pt2;
+       int             i;
+
+       if (acpi_wakeaddr == 0ul)
+               return;
+
+       sc->acpi_wakeaddr = acpi_wakeaddr;
+       sc->acpi_wakephys = vtophys(acpi_wakeaddr);
+
+       bcopy(wakecode, (void *)WAKECODE_VADDR(sc), sizeof(wakecode));
+
+       /* Patch GDT base address, ljmp targets and page table base address. */
+       WAKECODE_FIXUP((bootgdtdesc + 2), uint32_t,
+           WAKECODE_PADDR(sc) + bootgdt);
+       WAKECODE_FIXUP((wakeup_sw32 + 2), uint32_t,
+           WAKECODE_PADDR(sc) + wakeup_32);
+       WAKECODE_FIXUP((wakeup_sw64 + 1), uint32_t,
+           WAKECODE_PADDR(sc) + wakeup_64);
+       WAKECODE_FIXUP(wakeup_pagetables, uint32_t, sc->acpi_wakephys);
+
+       /* Save pointers to some global data. */
+       WAKECODE_FIXUP(wakeup_retaddr, void *, acpi_restorecpu);
+       WAKECODE_FIXUP(wakeup_kpml4, uint64_t, KPML4phys);
+       WAKECODE_FIXUP(wakeup_ctx, vm_offset_t,
+           WAKECODE_VADDR(sc) + wakeup_ctx);
+       WAKECODE_FIXUP(wakeup_efer, uint64_t, rdmsr(MSR_EFER));
+       WAKECODE_FIXUP(wakeup_pat, uint64_t, rdmsr(MSR_PAT));
+       WAKECODE_FIXUP(wakeup_star, uint64_t, rdmsr(MSR_STAR));
+       WAKECODE_FIXUP(wakeup_lstar, uint64_t, rdmsr(MSR_LSTAR));
+       WAKECODE_FIXUP(wakeup_cstar, uint64_t, rdmsr(MSR_CSTAR));
+       WAKECODE_FIXUP(wakeup_sfmask, uint64_t, rdmsr(MSR_SF_MASK));
+
+       /* Build temporary page tables below realmode code. */
+       pt4 = (uint64_t *)acpi_wakeaddr;
+       pt3 = pt4 + (PAGE_SIZE) / sizeof(uint64_t);
+       pt2 = pt3 + (PAGE_SIZE) / sizeof(uint64_t);
+
+       /* Create the initial 1GB replicated page tables */
+       for (i = 0; i < 512; i++) {
+               /*
+                * Each slot of the level 4 pages points
+                * to the same level 3 page
+                */
+               pt4[i] = (uint64_t)(sc->acpi_wakephys + PAGE_SIZE);
+               pt4[i] |= PG_V | PG_RW | PG_U;
+
+               /*
+                * Each slot of the level 3 pages points
+                * to the same level 2 page
+                */
+               pt3[i] = (uint64_t)(sc->acpi_wakephys + (2 * PAGE_SIZE));
+               pt3[i] |= PG_V | PG_RW | PG_U;
+
+               /* The level 2 page slots are mapped with 2MB pages for 1GB. */
+               pt2[i] = i * (2 * 1024 * 1024);
+               pt2[i] |= PG_V | PG_RW | PG_PS | PG_U;
+       }
+
+       if (bootverbose)
+               device_printf(sc->acpi_dev, "wakeup code va %p pa %p\n",
+                   (void *)sc->acpi_wakeaddr, (void *)sc->acpi_wakephys);
 }

Added: head/sys/amd64/acpica/genwakecode.sh
==============================================================================
--- /dev/null   00:00:00 1970   (empty, because file is newly added)
+++ head/sys/amd64/acpica/genwakecode.sh        Tue Mar 17 00:48:11 2009        
(r189903)
@@ -0,0 +1,6 @@
+#!/bin/sh
+# $FreeBSD$
+#
+file2c 'static char wakecode[] = {' '};' <acpi_wakecode.bin
+
+exit 0

Added: head/sys/amd64/acpica/genwakedata.sh
==============================================================================
--- /dev/null   00:00:00 1970   (empty, because file is newly added)
+++ head/sys/amd64/acpica/genwakedata.sh        Tue Mar 17 00:48:11 2009        
(r189903)
@@ -0,0 +1,9 @@
+#!/bin/sh
+# $FreeBSD$
+#
+nm -n --defined-only acpi_wakecode.o | while read offset dummy what
+do
+    echo "#define ${what}      0x${offset}"
+done
+
+exit 0

Modified: head/sys/amd64/amd64/amd64_mem.c
==============================================================================
--- head/sys/amd64/amd64/amd64_mem.c    Mon Mar 16 23:56:28 2009        
(r189902)
+++ head/sys/amd64/amd64/amd64_mem.c    Tue Mar 17 00:48:11 2009        
(r189903)
@@ -73,11 +73,13 @@ static void amd64_mrinit(struct mem_rang
 static int     amd64_mrset(struct mem_range_softc *sc,

*** DIFF OUTPUT TRUNCATED AT 1000 LINES ***
_______________________________________________
svn-src-all@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"

Reply via email to