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
This commit is contained in:
parent
11c15f90db
commit
e3fd0bc1b2
@ -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
|
||||
|
||||
/*
|
||||
* Set up segment registers for real mode again in case the
|
||||
* previous BIOS call clobbers them.
|
||||
*/
|
||||
movw %cs,%ax
|
||||
movw %ax,%ds
|
||||
movw %ax,%ss
|
||||
nobiosreset:
|
||||
/* When we reach here, int 0x10 should be ready. Hide cursor. */
|
||||
movb $0x01, %ah
|
||||
movb $0x20, %ch
|
||||
int $0x10
|
||||
|
||||
/* 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))
|
||||
|
||||
mov physical_esp,%esi /* to be used in 32bit code */
|
||||
|
||||
/* Enable protected mode */
|
||||
movl %cr0,%eax
|
||||
orl $(CR0_PE),%eax
|
||||
movl %eax,%cr0
|
||||
|
||||
wakeup_sw32:
|
||||
/* Switch to protected mode by intersegmental jump */
|
||||
ljmpl $KCSEL,$0x12345678 /* Code location, to be replaced */
|
||||
|
||||
/*
|
||||
* Now switched to protected mode without paging enabled.
|
||||
* %esi: KERNEL stack pointer (physical address)
|
||||
*/
|
||||
.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
|
||||
|
||||
/* Flush the prefetch queue */
|
||||
jmp 1f
|
||||
1: jmp 1f
|
||||
/* Re-start in case the previous BIOS call clobbers them. */
|
||||
jmp wakeup_start
|
||||
1:
|
||||
|
||||
/*
|
||||
* 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.
|
||||
* Find relocation base and patch the gdt descript and ljmp targets
|
||||
*/
|
||||
nop
|
||||
xorl %ebx, %ebx
|
||||
mov %cs, %bx
|
||||
sall $4, %ebx /* %ebx is now our relocation base */
|
||||
|
||||
movl %esi,%eax /* LDTR + TR */
|
||||
lldt %ax /* load LDT register */
|
||||
shrl $16,%eax
|
||||
ltr %ax /* load task register */
|
||||
/*
|
||||
* Load the descriptor table pointer. We'll need it when running
|
||||
* in 16-bit protected mode.
|
||||
*/
|
||||
lgdtl bootgdtdesc - wakeup_start
|
||||
|
||||
/* 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
|
||||
/* Enable protected mode */
|
||||
movl $CR0_PE, %eax
|
||||
mov %eax, %cr0
|
||||
|
||||
/* Jump to acpi_restorecpu() */
|
||||
/*
|
||||
* Now execute a far jump to turn on protected mode. This
|
||||
* causes the segment registers to turn into selectors and causes
|
||||
* %cs to be loaded from the gdt.
|
||||
*
|
||||
* The following instruction is:
|
||||
* ljmpl $bootcode32 - bootgdt, $wakeup_32 - wakeup_start
|
||||
* but gas cannot assemble that. And besides, we patch the targets
|
||||
* in early startup and its a little clearer what we are patching.
|
||||
*/
|
||||
wakeup_sw32:
|
||||
.byte 0x66 /* size override to 32 bits */
|
||||
.byte 0xea /* opcode for far jump */
|
||||
.long wakeup_32 - wakeup_start /* offset in segment */
|
||||
.word bootcode32 - bootgdt /* index in gdt for 32 bit code */
|
||||
|
||||
/*
|
||||
* At this point, we are running in 32 bit legacy protected mode.
|
||||
*/
|
||||
ALIGN_TEXT
|
||||
.code32
|
||||
wakeup_32:
|
||||
|
||||
mov $bootdata32 - bootgdt, %eax
|
||||
mov %ax, %ds
|
||||
|
||||
/* 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
|
||||
|
||||
/*
|
||||
* Finally, switch to long bit mode by enabling paging. We have
|
||||
* to be very careful here because all the segmentation disappears
|
||||
* out from underneath us. The spec says we can depend on the
|
||||
* subsequent pipelined branch to execute, but *only if* everthing
|
||||
* is still identity mapped. If any mappings change, the pipeline
|
||||
* will flush.
|
||||
*/
|
||||
mov %cr0, %eax
|
||||
orl $CR0_PG, %eax
|
||||
mov %eax, %cr0
|
||||
|
||||
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:
|
||||
|
@ -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 void *acpi_alloc_wakeup_handler(void);
|
||||
static void acpi_stop_beep(void *);
|
||||
|
||||
static struct region_descriptor __used saved_idt, saved_gdt;
|
||||
static struct region_descriptor *p_gdt;
|
||||
static uint16_t __used saved_ldt;
|
||||
#ifdef SMP
|
||||
static int acpi_wakeup_ap(struct acpi_softc *, int);
|
||||
static void acpi_wakeup_cpus(struct acpi_softc *, const cpuset_t *);
|
||||
#endif
|
||||
|
||||
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_printcpu(void)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
#define WAKECODE_FIXUP(offset, type, val) do { \
|
||||
type *addr; \
|
||||
addr = (type *)(sc->acpi_wakeaddr + offset); \
|
||||
*addr = val; \
|
||||
#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)
|
||||
|
||||
#define WAKECODE_BCOPY(offset, type, val) do { \
|
||||
void *addr; \
|
||||
addr = (void *)(sc->acpi_wakeaddr + offset); \
|
||||
bcopy(&(val), addr, sizeof(type)); \
|
||||
} while (0)
|
||||
|
||||
/* Turn off bits 1&2 of the PIT, stopping the beep. */
|
||||
static void
|
||||
acpi_stop_beep(void *arg)
|
||||
{
|
||||
outb(0x61, inb(0x61) & ~0x3);
|
||||
|
||||
if (acpi_resume_beep != 0)
|
||||
timer_spkr_release();
|
||||
}
|
||||
|
||||
#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);
|
||||
|
||||
/*
|
||||
* 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)
|
||||
|
||||
static void
|
||||
acpi_wakeup_cpus(struct acpi_softc *sc, const cpuset_t *wakeup_cpus)
|
||||
{
|
||||
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
|
||||
|
||||
ef = intr_disable();
|
||||
if (acpi_resume_beep != 0)
|
||||
timer_spkr_acquire();
|
||||
|
||||
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, int state)
|
||||
load_cr3(vtophys(pm->pm_pdir));
|
||||
#endif
|
||||
|
||||
ret_addr = 0;
|
||||
if (acpi_savecpu()) {
|
||||
/* Execute Sleep */
|
||||
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
|
||||
|
||||
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(resume_beep, uint8_t, (acpi_resume_beep != 0));
|
||||
WAKECODE_FIXUP(reset_video, uint8_t, (acpi_reset_video != 0));
|
||||
|
||||
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(wakeup_cr4, register_t, susppcbs[0]->pcb_cr4);
|
||||
WAKECODE_FIXUP(wakeup_cr3, register_t, susppcbs[0]->pcb_cr3);
|
||||
|
||||
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 (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;
|
||||
|
||||
bus_dmamap_load(sc->acpi_waketag, sc->acpi_wakemap,
|
||||
(void *)sc->acpi_wakeaddr, PAGE_SIZE, acpi_realmodeinst, sc, 0);
|
||||
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);
|
||||
|
||||
if (bootverbose)
|
||||
device_printf(sc->acpi_dev, "wakeup code va %p pa %p\n",
|
||||
(void *)sc->acpi_wakeaddr, (void *)sc->acpi_wakephys);
|
||||
}
|
||||
|
@ -333,6 +333,24 @@ IDTVEC(cpustop)
|
||||
POP_FRAME
|
||||
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.
|
||||
*
|
||||
|
@ -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));
|
||||
|
@ -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);
|
||||
|
@ -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)
|
||||
|
@ -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) {
|
||||
@ -1497,6 +1501,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.
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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_ */
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user