summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--sys/i386/acpica/acpi_wakecode.S322
-rw-r--r--sys/i386/acpica/acpi_wakeup.c479
-rw-r--r--sys/i386/i386/apic_vector.s18
-rw-r--r--sys/i386/i386/genassym.c13
-rw-r--r--sys/i386/i386/initcpu.c1
-rw-r--r--sys/i386/i386/machdep.c1
-rw-r--r--sys/i386/i386/mp_machdep.c36
-rw-r--r--sys/i386/i386/swtch.s137
-rw-r--r--sys/i386/include/apicvar.h3
-rw-r--r--sys/i386/include/md_var.h1
-rw-r--r--sys/i386/include/pcb.h14
-rw-r--r--sys/i386/include/smp.h2
-rw-r--r--sys/kern/subr_smp.c4
-rw-r--r--sys/pc98/pc98/machdep.c1
-rw-r--r--sys/sys/smp.h2
15 files changed, 604 insertions, 430 deletions
diff --git a/sys/i386/acpica/acpi_wakecode.S b/sys/i386/acpica/acpi_wakecode.S
index e23b138..27520f3 100644
--- a/sys/i386/acpica/acpi_wakecode.S
+++ b/sys/i386/acpica/acpi_wakecode.S
@@ -1,6 +1,8 @@
/*-
* Copyright (c) 2001 Takanori Watanabe <takawata@jp.freebsd.org>
- * Copyright (c) 2001 Mitsuru IWASAKI <iwasaki@jp.freebsd.org>
+ * Copyright (c) 2001-2012 Mitsuru IWASAKI <iwasaki@jp.freebsd.org>
+ * Copyright (c) 2003 Peter Wemm
+ * Copyright (c) 2008-2012 Jung-uk Kim <jkim@FreeBSD.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -28,7 +30,9 @@
*/
#include <machine/asmacros.h>
+#include <machine/ppireg.h>
#include <machine/specialreg.h>
+#include <machine/timerreg.h>
#include "assym.s"
@@ -39,221 +43,163 @@
* Depending on the previous sleep state, we may need to initialize more
* of the system (i.e., S3 suspend-to-RAM vs. S4 suspend-to-disk).
*/
- .align 4
- .code16
-wakeup_16:
- nop
- cli
- cld
+ .data /* So we can modify it */
+
+ ALIGN_TEXT
+ .code16
+wakeup_start:
/*
* Set up segment registers for real mode, a small stack for
* any calls we make, and clear any flags.
*/
- movw %cs,%ax
- movw %ax,%ds
- movw %ax,%ss
- movw $PAGE_SIZE,%sp
- pushl $0
- popfl
+ cli /* make sure no interrupts */
+ mov %cs, %ax /* copy %cs to %ds. Remember these */
+ mov %ax, %ds /* are offsets rather than selectors */
+ mov %ax, %ss
+ movw $PAGE_SIZE, %sp
+ xorw %ax, %ax
+ pushw %ax
+ popfw
/* To debug resume hangs, beep the speaker if the user requested. */
- cmpl $1,resume_beep
- jne nobeep
- movb $0xc0,%al
- outb %al,$0x42
- movb $0x04,%al
- outb %al,$0x42
- inb $0x61,%al
- orb $0x3,%al
- outb %al,$0x61
-nobeep:
+ testb $~0, resume_beep - wakeup_start
+ jz 1f
+ movb $0, resume_beep - wakeup_start
+
+ /* Set PIC timer2 to beep. */
+ movb $(TIMER_SEL2 | TIMER_SQWAVE | TIMER_16BIT), %al
+ outb %al, $TIMER_MODE
+
+ /* Turn on speaker. */
+ inb $IO_PPI, %al
+ orb $PIT_SPKR, %al
+ outb %al, $IO_PPI
+
+ /* Set frequency. */
+ movw $0x4c0, %ax
+ outb %al, $TIMER_CNTR2
+ shrw $8, %ax
+ outb %al, $TIMER_CNTR2
+1:
/* Re-initialize video BIOS if the reset_video tunable is set. */
- cmpl $1,reset_video
- jne nobiosreset
- lcall $0xc000,$3
+ testb $~0, reset_video - wakeup_start
+ jz 1f
+ movb $0, reset_video - wakeup_start
+ lcall $0xc000, $3
+
+ /* When we reach here, int 0x10 should be ready. Hide cursor. */
+ movb $0x01, %ah
+ movb $0x20, %ch
+ int $0x10
+
+ /* Re-start in case the previous BIOS call clobbers them. */
+ jmp wakeup_start
+1:
/*
- * Set up segment registers for real mode again in case the
- * previous BIOS call clobbers them.
+ * Find relocation base and patch the gdt descript and ljmp targets
*/
- movw %cs,%ax
- movw %ax,%ds
- movw %ax,%ss
-nobiosreset:
-
- /* Load GDT for real mode. Use 32 bit prefix for addresses >16 MB. */
- lgdtl physical_gdt
-
- /* Restore CR2, CR3 and CR4 */
- movl previous_cr2,%eax
- movl %eax,%cr2
- movl previous_cr3,%eax
- movl %eax,%cr3
- movl previous_cr4,%eax
- movl %eax,%cr4
+ xorl %ebx, %ebx
+ mov %cs, %bx
+ sall $4, %ebx /* %ebx is now our relocation base */
- /* 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))
-
- 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 */
+ mov $bootdata32 - bootgdt, %eax
+ mov %ax, %ds
- 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)
+ /* Get PCB and return address. */
+ movl wakeup_pcb - wakeup_start(%ebx), %esi
+ movl wakeup_ret - wakeup_start(%ebx), %edi
- /* 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
-
- /* Flush the prefetch queue */
- jmp 1f
-1: jmp 1f
-1:
+ /* 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 */
+ mov %cr0, %eax
+ orl $CR0_PG, %eax
+ mov %eax, %cr0
- /* 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
-
- /* 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
-
-/*
- * 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
+ .data
+
+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:
diff --git a/sys/i386/acpica/acpi_wakeup.c b/sys/i386/acpica/acpi_wakeup.c
index 31502f7..f3c1da7 100644
--- a/sys/i386/acpica/acpi_wakeup.c
+++ b/sys/i386/acpica/acpi_wakeup.c
@@ -1,6 +1,8 @@
/*-
* Copyright (c) 2001 Takanori Watanabe <takawata@jp.freebsd.org>
- * Copyright (c) 2001 Mitsuru IWASAKI <iwasaki@jp.freebsd.org>
+ * Copyright (c) 2001-2012 Mitsuru IWASAKI <iwasaki@jp.freebsd.org>
+ * Copyright (c) 2003 Peter Wemm
+ * Copyright (c) 2008-2012 Jung-uk Kim <jkim@FreeBSD.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -29,26 +31,29 @@
__FBSDID("$FreeBSD$");
#include <sys/param.h>
-#include <sys/systm.h>
-#include <sys/kernel.h>
#include <sys/bus.h>
-#include <sys/lock.h>
+#include <sys/eventhandler.h>
+#include <sys/kernel.h>
#include <sys/malloc.h>
#include <sys/memrange.h>
-#include <sys/proc.h>
-#include <sys/sysctl.h>
+#include <sys/smp.h>
#include <vm/vm.h>
#include <vm/pmap.h>
-#include <vm/vm_object.h>
-#include <vm/vm_page.h>
-#include <vm/vm_map.h>
-#include <machine/bus.h>
-#include <machine/cpufunc.h>
+#include <machine/clock.h>
#include <machine/intr_machdep.h>
#include <x86/mca.h>
-#include <machine/segments.h>
+#include <machine/pcb.h>
+#include <machine/pmap.h>
+#include <machine/specialreg.h>
+#include <machine/md_var.h>
+
+#ifdef SMP
+#include <x86/apicreg.h>
+#include <machine/smp.h>
+#include <machine/vmparam.h>
+#endif
#include <contrib/dev/acpica/include/acpi.h>
@@ -57,164 +62,186 @@ __FBSDID("$FreeBSD$");
#include "acpi_wakecode.h"
#include "acpi_wakedata.h"
-/* Make sure the code is less than one page and leave room for the stack. */
+/* Make sure the code is less than a page and leave room for the stack. */
CTASSERT(sizeof(wakecode) < PAGE_SIZE - 1024);
-#ifndef _SYS_CDEFS_H_
-#error this file needs sys/cdefs.h as a prerequisite
+extern int acpi_resume_beep;
+extern int acpi_reset_video;
+
+#ifdef SMP
+extern struct pcb **susppcbs;
+#else
+static struct pcb **susppcbs;
#endif
-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;
-#define WAKECODE_BCOPY(offset, type, val) do { \
- void *addr; \
- addr = (void *)(sc->acpi_wakeaddr + offset); \
- bcopy(&(val), addr, sizeof(type)); \
-} while (0)
+ 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);
+
+ /*
+ * 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();
+
+ AcpiSetFirmwareWakingVector(WAKECODE_PADDR(sc));
- ef = intr_disable();
+ 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, int state)
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);
+ 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(where_to_recover, void *, acpi_restorecpu);
+ WAKECODE_FIXUP(resume_beep, uint8_t, (acpi_resume_beep != 0));
+ WAKECODE_FIXUP(reset_video, uint8_t, (acpi_reset_video != 0));
- 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);
+ 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, int state)
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, int state)
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));
+
+ /* 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);
- bus_dmamap_load(sc->acpi_waketag, sc->acpi_wakemap,
- (void *)sc->acpi_wakeaddr, PAGE_SIZE, acpi_realmodeinst, sc, 0);
+ if (bootverbose)
+ device_printf(sc->acpi_dev, "wakeup code va %p pa %p\n",
+ (void *)sc->acpi_wakeaddr, (void *)sc->acpi_wakephys);
}
diff --git a/sys/i386/i386/apic_vector.s b/sys/i386/i386/apic_vector.s
index 86e78c4..7e556f2 100644
--- a/sys/i386/i386/apic_vector.s
+++ b/sys/i386/i386/apic_vector.s
@@ -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.
diff --git a/sys/i386/i386/genassym.c b/sys/i386/i386/genassym.c
index 9acc4f4..2394c09 100644
--- a/sys/i386/i386/genassym.c
+++ b/sys/i386/i386/genassym.c
@@ -121,7 +121,10 @@ ASSYM(VM_MAXUSER_ADDRESS, VM_MAXUSER_ADDRESS);
ASSYM(KERNBASE, KERNBASE);
ASSYM(KERNLOAD, KERNLOAD);
ASSYM(MCLBYTES, MCLBYTES);
+ASSYM(PCB_CR0, offsetof(struct pcb, pcb_cr0));
+ASSYM(PCB_CR2, offsetof(struct pcb, pcb_cr2));
ASSYM(PCB_CR3, offsetof(struct pcb, pcb_cr3));
+ASSYM(PCB_CR4, offsetof(struct pcb, pcb_cr4));
ASSYM(PCB_EDI, offsetof(struct pcb, pcb_edi));
ASSYM(PCB_ESI, offsetof(struct pcb, pcb_esi));
ASSYM(PCB_EBP, offsetof(struct pcb, pcb_ebp));
@@ -130,7 +133,11 @@ ASSYM(PCB_EBX, offsetof(struct pcb, pcb_ebx));
ASSYM(PCB_EIP, offsetof(struct pcb, pcb_eip));
ASSYM(TSS_ESP0, offsetof(struct i386tss, tss_esp0));
+ASSYM(PCB_DS, offsetof(struct pcb, pcb_ds));
+ASSYM(PCB_ES, offsetof(struct pcb, pcb_es));
+ASSYM(PCB_FS, offsetof(struct pcb, pcb_fs));
ASSYM(PCB_GS, offsetof(struct pcb, pcb_gs));
+ASSYM(PCB_SS, offsetof(struct pcb, pcb_ss));
ASSYM(PCB_DR0, offsetof(struct pcb, pcb_dr0));
ASSYM(PCB_DR1, offsetof(struct pcb, pcb_dr1));
ASSYM(PCB_DR2, offsetof(struct pcb, pcb_dr2));
@@ -143,6 +150,7 @@ ASSYM(PCB_DBREGS, PCB_DBREGS);
ASSYM(PCB_EXT, offsetof(struct pcb, pcb_ext));
ASSYM(PCB_FSD, offsetof(struct pcb, pcb_fsd));
+ASSYM(PCB_GSD, offsetof(struct pcb, pcb_gsd));
ASSYM(PCB_VM86, offsetof(struct pcb, pcb_vm86));
ASSYM(PCB_FLAGS, offsetof(struct pcb, pcb_flags));
ASSYM(PCB_SAVEFPU, offsetof(struct pcb, pcb_save));
@@ -152,6 +160,11 @@ ASSYM(PCB_ONFAULT, offsetof(struct pcb, pcb_onfault));
ASSYM(PCB_SIZE, sizeof(struct pcb));
ASSYM(PCB_VM86CALL, PCB_VM86CALL);
+ASSYM(PCB_GDT, offsetof(struct pcb, pcb_gdt));
+ASSYM(PCB_IDT, offsetof(struct pcb, pcb_idt));
+ASSYM(PCB_LDT, offsetof(struct pcb, pcb_ldt));
+ASSYM(PCB_TR, offsetof(struct pcb, pcb_tr));
+
ASSYM(TF_TRAPNO, offsetof(struct trapframe, tf_trapno));
ASSYM(TF_ERR, offsetof(struct trapframe, tf_err));
ASSYM(TF_EIP, offsetof(struct trapframe, tf_eip));
diff --git a/sys/i386/i386/initcpu.c b/sys/i386/i386/initcpu.c
index ec77adb..97454bc 100644
--- a/sys/i386/i386/initcpu.c
+++ b/sys/i386/i386/initcpu.c
@@ -48,7 +48,6 @@ __FBSDID("$FreeBSD$");
#define CPU_ENABLE_SSE
#endif
-void initializecpu(void);
#if defined(I586_CPU) && defined(CPU_WT_ALLOC)
void enable_K5_wt_alloc(void);
void enable_K6_wt_alloc(void);
diff --git a/sys/i386/i386/machdep.c b/sys/i386/i386/machdep.c
index fb6ad62..f0546b0 100644
--- a/sys/i386/i386/machdep.c
+++ b/sys/i386/i386/machdep.c
@@ -180,7 +180,6 @@ extern void dblfault_handler(void);
extern void printcpuinfo(void); /* XXX header file */
extern void finishidentcpu(void);
extern void panicifcpuunsupported(void);
-extern void initializecpu(void);
#define CS_SECURE(cs) (ISPL(cs) == SEL_UPL)
#define EFL_SECURE(ef, oef) ((((ef) ^ (oef)) & ~PSL_USERCHANGE) == 0)
diff --git a/sys/i386/i386/mp_machdep.c b/sys/i386/i386/mp_machdep.c
index 0bf6e22..d70b4be 100644
--- a/sys/i386/i386/mp_machdep.c
+++ b/sys/i386/i386/mp_machdep.c
@@ -146,6 +146,7 @@ void *bootstacks[MAXCPU];
static void *dpcpu;
struct pcb stoppcbs[MAXCPU];
+struct pcb **susppcbs = NULL;
/* Variables needed for SMP tlb shootdown. */
vm_offset_t smp_tlb_addr1;
@@ -587,6 +588,9 @@ cpu_mp_start(void)
setidt(IPI_STOP, IDTVEC(cpustop),
SDT_SYS386IGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL));
+ /* Install an inter-CPU IPI for CPU suspend/resume */
+ setidt(IPI_SUSPEND, IDTVEC(cpususpend),
+ SDT_SYS386IGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL));
/* Set boot_cpu_id if needed. */
if (boot_cpu_id == -1) {
@@ -1498,6 +1502,38 @@ cpustop_handler(void)
}
/*
+ * Handle an IPI_SUSPEND by saving our current context and spinning until we
+ * are resumed.
+ */
+void
+cpususpend_handler(void)
+{
+ u_int cpu;
+
+ cpu = PCPU_GET(cpuid);
+
+ if (suspendctx(susppcbs[cpu])) {
+ wbinvd();
+ CPU_SET_ATOMIC(cpu, &stopped_cpus);
+ } else {
+ pmap_init_pat();
+ PCPU_SET(switchtime, 0);
+ PCPU_SET(switchticks, ticks);
+ susppcbs[cpu]->pcb_eip = 0;
+ }
+
+ /* Wait for resume */
+ while (!CPU_ISSET(cpu, &started_cpus))
+ ia32_pause();
+
+ CPU_CLR_ATOMIC(cpu, &started_cpus);
+ CPU_CLR_ATOMIC(cpu, &stopped_cpus);
+
+ /* Resume MCA and local APIC */
+ mca_resume();
+ lapic_setup(0);
+}
+/*
* This is called once the rest of the system is up and running and we're
* ready to let the AP's out of the pen.
*/
diff --git a/sys/i386/i386/swtch.s b/sys/i386/i386/swtch.s
index 680b032..c53abe2 100644
--- a/sys/i386/i386/swtch.s
+++ b/sys/i386/i386/swtch.s
@@ -427,3 +427,140 @@ ENTRY(savectx)
ret
END(savectx)
+
+/*
+ * suspendctx(pcb)
+ * Update pcb, suspending current processor state.
+ */
+ENTRY(suspendctx)
+ /* Fetch PCB. */
+ movl 4(%esp),%ecx
+
+ /* Save context by calling savectx(). */
+ pushl %ecx
+ call savectx
+ addl $4,%esp
+
+ /* Fetch PCB again. */
+ movl 4(%esp),%ecx
+
+ /* Update caller's return address and stack pointer. */
+ movl (%esp),%eax
+ movl %eax,PCB_EIP(%ecx)
+ movl %esp,PCB_ESP(%ecx)
+
+ /* Save other registers and descriptor tables. */
+ movl %cr0,%eax
+ movl %eax,PCB_CR0(%ecx)
+ movl %cr2,%eax
+ movl %eax,PCB_CR2(%ecx)
+ movl %cr4,%eax
+ movl %eax,PCB_CR4(%ecx)
+
+ movl %dr0,%eax
+ movl %eax,PCB_DR0(%ecx)
+ movl %dr1,%eax
+ movl %eax,PCB_DR1(%ecx)
+ movl %dr2,%eax
+ movl %eax,PCB_DR2(%ecx)
+ movl %dr3,%eax
+ movl %eax,PCB_DR3(%ecx)
+ movl %dr6,%eax
+ movl %eax,PCB_DR6(%ecx)
+ movl %dr7,%eax
+ movl %eax,PCB_DR7(%ecx)
+
+ mov %ds,PCB_DS(%ecx)
+ mov %es,PCB_ES(%ecx)
+ mov %fs,PCB_FS(%ecx)
+ mov %ss,PCB_SS(%ecx)
+
+ sgdt PCB_GDT(%ecx)
+ sidt PCB_IDT(%ecx)
+ sldt PCB_LDT(%ecx)
+ str PCB_TR(%ecx)
+
+ movl $1,%eax
+ ret
+END(suspendctx)
+
+/*
+ * resumectx(pcb in %esi)
+ * Resuming processor state from pcb.
+ */
+ENTRY(resumectx)
+ /* Fetch PCB. */
+ movl %esi,%ecx
+
+ /* Restore GDT. */
+ lgdt PCB_GDT(%ecx)
+
+ /* Restore segment registers */
+ movzwl PCB_DS(%ecx),%eax
+ mov %ax,%ds
+ movzwl PCB_ES(%ecx),%eax
+ mov %ax,%es
+ movzwl PCB_FS(%ecx),%eax
+ mov %ax,%fs
+ movzwl PCB_GS(%ecx),%eax
+ movw %ax,%gs
+ movzwl PCB_SS(%ecx),%eax
+ mov %ax,%ss
+
+ /* Restore CR2, CR4, CR3 and CR0 */
+ movl PCB_CR2(%ecx),%eax
+ movl %eax,%cr2
+ movl PCB_CR4(%ecx),%eax
+ movl %eax,%cr4
+ movl PCB_CR3(%ecx),%eax
+ movl %eax,%cr3
+ movl PCB_CR0(%ecx),%eax
+ movl %eax,%cr0
+ jmp 1f
+1:
+
+ /* Restore descriptor tables */
+ lidt PCB_IDT(%ecx)
+ lldt PCB_LDT(%ecx)
+
+#define SDT_SYS386TSS 9
+#define SDT_SYS386BSY 11
+ /* Clear "task busy" bit and reload TR */
+ movl PCPU(TSS_GDT),%eax
+ andb $(~SDT_SYS386BSY | SDT_SYS386TSS),5(%eax)
+ movzwl PCB_TR(%ecx),%eax
+ ltr %ax
+#undef SDT_SYS386TSS
+#undef SDT_SYS386BSY
+
+ /* Restore debug registers */
+ movl PCB_DR0(%ecx),%eax
+ movl %eax,%dr0
+ movl PCB_DR1(%ecx),%eax
+ movl %eax,%dr1
+ movl PCB_DR2(%ecx),%eax
+ movl %eax,%dr2
+ movl PCB_DR3(%ecx),%eax
+ movl %eax,%dr3
+ movl PCB_DR6(%ecx),%eax
+ movl %eax,%dr6
+ movl PCB_DR7(%ecx),%eax
+ movl %eax,%dr7
+
+#ifdef DEV_NPX
+ /* XXX FIX ME */
+#endif
+
+ /* Restore other registers */
+ movl PCB_EDI(%ecx),%edi
+ movl PCB_ESI(%ecx),%esi
+ movl PCB_EBP(%ecx),%ebp
+ movl PCB_ESP(%ecx),%esp
+ movl PCB_EBX(%ecx),%ebx
+
+ /* reload code selector by turning return into intersegmental return */
+ pushl PCB_EIP(%ecx)
+ movl $KCSEL,4(%esp)
+ xorl %eax,%eax
+ lret
+END(resumectx)
diff --git a/sys/i386/include/apicvar.h b/sys/i386/include/apicvar.h
index a12e36a..a0e622e 100644
--- a/sys/i386/include/apicvar.h
+++ b/sys/i386/include/apicvar.h
@@ -126,7 +126,8 @@
#define IPI_IS_BITMAPED(x) ((x) <= IPI_BITMAP_LAST)
#define IPI_STOP (APIC_IPI_INTS + 7) /* Stop CPU until restarted. */
-#define IPI_STOP_HARD (APIC_IPI_INTS + 8) /* Stop CPU with a NMI. */
+#define IPI_SUSPEND (APIC_IPI_INTS + 8) /* Suspend CPU until restarted. */
+#define IPI_STOP_HARD (APIC_IPI_INTS + 9) /* Stop CPU with a NMI. */
/*
* The spurious interrupt can share the priority class with the IPIs since
diff --git a/sys/i386/include/md_var.h b/sys/i386/include/md_var.h
index 168ee62..3563e6c 100644
--- a/sys/i386/include/md_var.h
+++ b/sys/i386/include/md_var.h
@@ -91,6 +91,7 @@ void doreti_popl_fs(void) __asm(__STRING(doreti_popl_fs));
void doreti_popl_fs_fault(void) __asm(__STRING(doreti_popl_fs_fault));
void dump_add_page(vm_paddr_t);
void dump_drop_page(vm_paddr_t);
+void initializecpu(void);
void enable_sse(void);
void fillw(int /*u_short*/ pat, void *base, size_t cnt);
void i686_pagezero(void *addr);
diff --git a/sys/i386/include/pcb.h b/sys/i386/include/pcb.h
index 4c227df..f25167d 100644
--- a/sys/i386/include/pcb.h
+++ b/sys/i386/include/pcb.h
@@ -45,7 +45,10 @@
#include <machine/npx.h>
struct pcb {
+ int pcb_cr0;
+ int pcb_cr2;
int pcb_cr3;
+ int pcb_cr4;
int pcb_edi;
int pcb_esi;
int pcb_ebp;
@@ -71,13 +74,22 @@ struct pcb {
#define PCB_KERNNPX 0x40 /* kernel uses npx */
caddr_t pcb_onfault; /* copyin/out fault recovery */
+ int pcb_ds;
+ int pcb_es;
+ int pcb_fs;
int pcb_gs;
+ int pcb_ss;
struct segment_descriptor pcb_fsd;
struct segment_descriptor pcb_gsd;
struct pcb_ext *pcb_ext; /* optional pcb extension */
int pcb_psl; /* process status long */
u_long pcb_vm86[2]; /* vm86bios scratch space */
union savefpu *pcb_save;
+
+ struct region_descriptor pcb_gdt;
+ struct region_descriptor pcb_idt;
+ uint16_t pcb_ldt;
+ uint16_t pcb_tr;
};
#ifdef _KERNEL
@@ -85,6 +97,8 @@ struct trapframe;
void makectx(struct trapframe *, struct pcb *);
void savectx(struct pcb *) __returns_twice;
+int suspendctx(struct pcb *) __returns_twice;
+void resumectx(struct pcb *);
#endif
#endif /* _I386_PCB_H_ */
diff --git a/sys/i386/include/smp.h b/sys/i386/include/smp.h
index 04d67c9..43172d6 100644
--- a/sys/i386/include/smp.h
+++ b/sys/i386/include/smp.h
@@ -53,12 +53,14 @@ inthand_t
IDTVEC(invlcache), /* Write back and invalidate cache */
IDTVEC(ipi_intr_bitmap_handler), /* Bitmap based IPIs */
IDTVEC(cpustop), /* CPU stops & waits to be restarted */
+ IDTVEC(cpususpend), /* CPU suspends & waits to be resumed */
IDTVEC(rendezvous), /* handle CPU rendezvous */
IDTVEC(lazypmap); /* handle lazy pmap release */
/* functions in mp_machdep.c */
void cpu_add(u_int apic_id, char boot_cpu);
void cpustop_handler(void);
+void cpususpend_handler(void);
void init_secondary(void);
void ipi_all_but_self(u_int ipi);
#ifndef XEN
diff --git a/sys/kern/subr_smp.c b/sys/kern/subr_smp.c
index 5dd0c55..8edd8af 100644
--- a/sys/kern/subr_smp.c
+++ b/sys/kern/subr_smp.c
@@ -209,7 +209,7 @@ generic_stop_cpus(cpuset_t map, u_int type)
int i;
KASSERT(
-#if defined(__amd64__)
+#if defined(__amd64__) || defined(__i386__)
type == IPI_STOP || type == IPI_STOP_HARD || type == IPI_SUSPEND,
#else
type == IPI_STOP || type == IPI_STOP_HARD,
@@ -260,7 +260,7 @@ stop_cpus_hard(cpuset_t map)
return (generic_stop_cpus(map, IPI_STOP_HARD));
}
-#if defined(__amd64__)
+#if defined(__amd64__) || defined(__i386__)
int
suspend_cpus(cpuset_t map)
{
diff --git a/sys/pc98/pc98/machdep.c b/sys/pc98/pc98/machdep.c
index 691d21f..88b1f20 100644
--- a/sys/pc98/pc98/machdep.c
+++ b/sys/pc98/pc98/machdep.c
@@ -151,7 +151,6 @@ extern void dblfault_handler(void);
extern void printcpuinfo(void); /* XXX header file */
extern void finishidentcpu(void);
extern void panicifcpuunsupported(void);
-extern void initializecpu(void);
#define CS_SECURE(cs) (ISPL(cs) == SEL_UPL)
#define EFL_SECURE(ef, oef) ((((ef) ^ (oef)) & ~PSL_USERCHANGE) == 0)
diff --git a/sys/sys/smp.h b/sys/sys/smp.h
index 66e8008..8c7ca57 100644
--- a/sys/sys/smp.h
+++ b/sys/sys/smp.h
@@ -163,7 +163,7 @@ void forward_signal(struct thread *);
int restart_cpus(cpuset_t);
int stop_cpus(cpuset_t);
int stop_cpus_hard(cpuset_t);
-#if defined(__amd64__)
+#if defined(__amd64__) || defined(__i386__)
int suspend_cpus(cpuset_t);
#endif
void smp_rendezvous_action(void);
OpenPOWER on IntegriCloud