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 <takaw...@jp.freebsd.org>
- * Copyright (c) 2001 Mitsuru IWASAKI <iwas...@jp.freebsd.org>
+ * Copyright (c) 2001-2012 Mitsuru IWASAKI <iwas...@jp.freebsd.org>
+ * Copyright (c) 2003 Peter Wemm
+ * Copyright (c) 2008-2012 Jung-uk Kim <j...@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 <takaw...@jp.freebsd.org>
- * Copyright (c) 2001 Mitsuru IWASAKI <iwas...@jp.freebsd.org>
+ * Copyright (c) 2001-2012 Mitsuru IWASAKI <iwas...@jp.freebsd.org>
+ * Copyright (c) 2003 Peter Wemm
+ * Copyright (c) 2008-2012 Jung-uk Kim <j...@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 ***
_______________________________________________
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