svn commit: r235796 - in stable/9/sys: i386/acpica i386/i386
i386/include kern pc98/pc98 sys
Mitsuru IWASAKI
iwasaki at FreeBSD.org
Tue May 22 17:44:01 UTC 2012
Author: iwasaki
Date: Tue May 22 17:44:01 2012
New Revision: 235796
URL: http://svn.freebsd.org/changeset/base/235796
Log:
MFC 235622,235639,235683:
Merge SMP/i386 suspend/resume support.
Modified:
stable/9/sys/i386/acpica/acpi_wakecode.S
stable/9/sys/i386/acpica/acpi_wakeup.c
stable/9/sys/i386/i386/apic_vector.s
stable/9/sys/i386/i386/genassym.c
stable/9/sys/i386/i386/initcpu.c
stable/9/sys/i386/i386/machdep.c
stable/9/sys/i386/i386/mp_machdep.c
stable/9/sys/i386/i386/swtch.s
stable/9/sys/i386/include/apicvar.h
stable/9/sys/i386/include/md_var.h
stable/9/sys/i386/include/pcb.h
stable/9/sys/i386/include/smp.h
stable/9/sys/kern/subr_smp.c
stable/9/sys/pc98/pc98/machdep.c
stable/9/sys/sys/smp.h
Directory Properties:
stable/9/sys/ (props changed)
Modified: stable/9/sys/i386/acpica/acpi_wakecode.S
==============================================================================
--- stable/9/sys/i386/acpica/acpi_wakecode.S Tue May 22 17:30:02 2012 (r235795)
+++ stable/9/sys/i386/acpica/acpi_wakecode.S Tue May 22 17:44:01 2012 (r235796)
@@ -1,6 +1,8 @@
/*-
* Copyright (c) 2001 Takanori Watanabe <takawata at jp.freebsd.org>
- * Copyright (c) 2001 Mitsuru IWASAKI <iwasaki at jp.freebsd.org>
+ * Copyright (c) 2001-2012 Mitsuru IWASAKI <iwasaki at jp.freebsd.org>
+ * Copyright (c) 2003 Peter Wemm
+ * Copyright (c) 2008-2012 Jung-uk Kim <jkim at 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: stable/9/sys/i386/acpica/acpi_wakeup.c
==============================================================================
--- stable/9/sys/i386/acpica/acpi_wakeup.c Tue May 22 17:30:02 2012 (r235795)
+++ stable/9/sys/i386/acpica/acpi_wakeup.c Tue May 22 17:44:01 2012 (r235796)
@@ -1,6 +1,8 @@
/*-
* Copyright (c) 2001 Takanori Watanabe <takawata at jp.freebsd.org>
- * Copyright (c) 2001 Mitsuru IWASAKI <iwasaki at jp.freebsd.org>
+ * Copyright (c) 2001-2012 Mitsuru IWASAKI <iwasaki at jp.freebsd.org>
+ * Copyright (c) 2003 Peter Wemm
+ * Copyright (c) 2008-2012 Jung-uk Kim <jkim at 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
-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 */
+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
+
+#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));
- if (bootverbose)
- acpi_printcpu();
+ WAKECODE_FIXUP(wakeup_cr4, register_t, susppcbs[0]->pcb_cr4);
+ WAKECODE_FIXUP(wakeup_cr3, register_t, susppcbs[0]->pcb_cr3);
+
+ 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;
+
+ wakeaddr = acpi_alloc_wakeup_handler();
+ if (wakeaddr == NULL)
return;
- sc->acpi_waketag = acpi_waketag;
- sc->acpi_wakeaddr = acpi_wakeaddr;
- sc->acpi_wakemap = acpi_wakemap;
+ sc->acpi_wakeaddr = (vm_offset_t)wakeaddr;
+ sc->acpi_wakephys = vtophys(wakeaddr);
- bus_dmamap_load(sc->acpi_waketag, sc->acpi_wakemap,
- (void *)sc->acpi_wakeaddr, PAGE_SIZE, acpi_realmodeinst, sc, 0);
+ bcopy(wakecode, (void *)WAKECODE_VADDR(sc), sizeof(wakecode));
+
+ /* 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 %#x pa %#jx\n",
+ sc->acpi_wakeaddr, (uintmax_t)sc->acpi_wakephys);
}
Modified: stable/9/sys/i386/i386/apic_vector.s
==============================================================================
--- stable/9/sys/i386/i386/apic_vector.s Tue May 22 17:30:02 2012 (r235795)
+++ stable/9/sys/i386/i386/apic_vector.s Tue May 22 17:44:01 2012 (r235796)
@@ -334,6 +334,26 @@ IDTVEC(cpustop)
iret
/*
+ * Executed by a CPU when it receives an IPI_SUSPEND from another CPU.
+ */
+#ifndef XEN
+ .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
+#endif
+
+/*
* Executed by a CPU when it receives a RENDEZVOUS IPI from another CPU.
*
* - Calls the generic rendezvous action function.
Modified: stable/9/sys/i386/i386/genassym.c
==============================================================================
--- stable/9/sys/i386/i386/genassym.c Tue May 22 17:30:02 2012 (r235795)
+++ stable/9/sys/i386/i386/genassym.c Tue May 22 17:44:01 2012 (r235796)
@@ -121,7 +121,10 @@ ASSYM(VM_MAXUSER_ADDRESS, VM_MAXUSER_ADD
ASSYM(KERNBASE, KERNBASE);
*** DIFF OUTPUT TRUNCATED AT 1000 LINES ***
More information about the svn-src-stable-9
mailing list