From owner-svn-src-head@FreeBSD.ORG Fri May 18 18:55:59 2012 Return-Path: Delivered-To: svn-src-head@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [69.147.83.52]) by hub.freebsd.org (Postfix) with ESMTP id 542FE1065673; Fri, 18 May 2012 18:55:59 +0000 (UTC) (envelope-from iwasaki@FreeBSD.org) Received: from svn.freebsd.org (svn.freebsd.org [IPv6:2001:4f8:fff6::2c]) by mx1.freebsd.org (Postfix) with ESMTP id 3C9CA8FC19; Fri, 18 May 2012 18:55:59 +0000 (UTC) Received: from svn.freebsd.org (localhost [127.0.0.1]) by svn.freebsd.org (8.14.4/8.14.4) with ESMTP id q4IItxev069718; Fri, 18 May 2012 18:55:59 GMT (envelope-from iwasaki@svn.freebsd.org) Received: (from iwasaki@localhost) by svn.freebsd.org (8.14.4/8.14.4/Submit) id q4IItxs0069711; Fri, 18 May 2012 18:55:59 GMT (envelope-from iwasaki@svn.freebsd.org) Message-Id: <201205181855.q4IItxs0069711@svn.freebsd.org> From: Mitsuru IWASAKI Date: Fri, 18 May 2012 18:55:59 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org X-SVN-Group: head MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cc: Subject: svn commit: r235622 - in head/sys: i386/acpica i386/i386 i386/include kern pc98/pc98 sys X-BeenThere: svn-src-head@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: SVN commit messages for the src tree for head/-current List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Fri, 18 May 2012 18:55:59 -0000 Author: iwasaki Date: Fri May 18 18:55:58 2012 New Revision: 235622 URL: http://svn.freebsd.org/changeset/base/235622 Log: Add SMP/i386 suspend/resume support. Most part is merged from amd64. - i386/acpica/acpi_wakecode.S Replaced with amd64 code (from realmode to paging enabling code). - i386/acpica/acpi_wakeup.c Replaced with amd64 code (except for wakeup_pagetables stuff). - i386/include/pcb.h - i386/i386/genassym.c Added PCB new members (CR0, CR2, CR4, DS, ED, FS, SS, GDT, IDT, LDT and TR) needed for suspend/resume, not for context switch. - i386/i386/swtch.s Added suspendctx() and resumectx(). Note that savectx() was not changed and used for suspending (while amd64 code uses it). BSP and AP execute the same sequence, suspendctx(), acpi_wakecode() and resumectx() for suspend/resume (in case of UP system also). - i386/i386/apic_vector.s Added cpususpend(). - i386/i386/mp_machdep.c - i386/include/smp.h Added cpususpend_handler(). - i386/include/apicvar.h - kern/subr_smp.c - sys/smp.h Added IPI_SUSPEND and suspend_cpus(). - i386/i386/initcpu.c - i386/i386/machdep.c - i386/include/md_var.h - pc98/pc98/machdep.c Moved initializecpu() declarations to md_var.h. MFC after: 3 days Modified: head/sys/i386/acpica/acpi_wakecode.S head/sys/i386/acpica/acpi_wakeup.c head/sys/i386/i386/apic_vector.s head/sys/i386/i386/genassym.c head/sys/i386/i386/initcpu.c head/sys/i386/i386/machdep.c head/sys/i386/i386/mp_machdep.c head/sys/i386/i386/swtch.s head/sys/i386/include/apicvar.h head/sys/i386/include/md_var.h head/sys/i386/include/pcb.h head/sys/i386/include/smp.h head/sys/kern/subr_smp.c head/sys/pc98/pc98/machdep.c head/sys/sys/smp.h Modified: head/sys/i386/acpica/acpi_wakecode.S ============================================================================== --- head/sys/i386/acpica/acpi_wakecode.S Fri May 18 18:53:28 2012 (r235621) +++ head/sys/i386/acpica/acpi_wakecode.S Fri May 18 18:55:58 2012 (r235622) @@ -1,6 +1,8 @@ /*- * Copyright (c) 2001 Takanori Watanabe - * Copyright (c) 2001 Mitsuru IWASAKI + * Copyright (c) 2001-2012 Mitsuru IWASAKI + * Copyright (c) 2003 Peter Wemm + * Copyright (c) 2008-2012 Jung-uk Kim * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -28,7 +30,9 @@ */ #include +#include #include +#include #include "assym.s" @@ -39,221 +43,163 @@ * Depending on the previous sleep state, we may need to initialize more * of the system (i.e., S3 suspend-to-RAM vs. S4 suspend-to-disk). */ - .align 4 - .code16 -wakeup_16: - nop - cli - cld + .data /* So we can modify it */ + + ALIGN_TEXT + .code16 +wakeup_start: /* * Set up segment registers for real mode, a small stack for * any calls we make, and clear any flags. */ - movw %cs,%ax - movw %ax,%ds - movw %ax,%ss - movw $PAGE_SIZE,%sp - pushl $0 - popfl + cli /* make sure no interrupts */ + mov %cs, %ax /* copy %cs to %ds. Remember these */ + mov %ax, %ds /* are offsets rather than selectors */ + mov %ax, %ss + movw $PAGE_SIZE, %sp + xorw %ax, %ax + pushw %ax + popfw /* To debug resume hangs, beep the speaker if the user requested. */ - cmpl $1,resume_beep - jne nobeep - movb $0xc0,%al - outb %al,$0x42 - movb $0x04,%al - outb %al,$0x42 - inb $0x61,%al - orb $0x3,%al - outb %al,$0x61 -nobeep: + testb $~0, resume_beep - wakeup_start + jz 1f + movb $0, resume_beep - wakeup_start + + /* Set PIC timer2 to beep. */ + movb $(TIMER_SEL2 | TIMER_SQWAVE | TIMER_16BIT), %al + outb %al, $TIMER_MODE + + /* Turn on speaker. */ + inb $IO_PPI, %al + orb $PIT_SPKR, %al + outb %al, $IO_PPI + + /* Set frequency. */ + movw $0x4c0, %ax + outb %al, $TIMER_CNTR2 + shrw $8, %ax + outb %al, $TIMER_CNTR2 +1: /* Re-initialize video BIOS if the reset_video tunable is set. */ - cmpl $1,reset_video - jne nobiosreset - lcall $0xc000,$3 + testb $~0, reset_video - wakeup_start + jz 1f + movb $0, reset_video - wakeup_start + lcall $0xc000, $3 + + /* When we reach here, int 0x10 should be ready. Hide cursor. */ + movb $0x01, %ah + movb $0x20, %ch + int $0x10 + + /* Re-start in case the previous BIOS call clobbers them. */ + jmp wakeup_start +1: /* - * Set up segment registers for real mode again in case the - * previous BIOS call clobbers them. + * Find relocation base and patch the gdt descript and ljmp targets */ - movw %cs,%ax - movw %ax,%ds - movw %ax,%ss -nobiosreset: - - /* Load GDT for real mode. Use 32 bit prefix for addresses >16 MB. */ - lgdtl physical_gdt - - /* Restore CR2, CR3 and CR4 */ - movl previous_cr2,%eax - movl %eax,%cr2 - movl previous_cr3,%eax - movl %eax,%cr3 - movl previous_cr4,%eax - movl %eax,%cr4 - - /* Transfer some values to protected mode with an inline stack */ -#define NVALUES 9 -#define TRANSFER_STACK32(val, idx) \ - movl val,%eax; \ - movl %eax,wakeup_32stack+(idx+1)+(idx*4) - - TRANSFER_STACK32(previous_ss, (NVALUES - 9)) - TRANSFER_STACK32(previous_fs, (NVALUES - 8)) - TRANSFER_STACK32(previous_ds, (NVALUES - 7)) - TRANSFER_STACK32(physical_gdt+2, (NVALUES - 6)) - TRANSFER_STACK32(where_to_recover, (NVALUES - 5)) - TRANSFER_STACK32(previous_idt+2, (NVALUES - 4)) - TRANSFER_STACK32(previous_ldt, (NVALUES - 3)) - TRANSFER_STACK32(previous_gdt+2, (NVALUES - 2)) - TRANSFER_STACK32(previous_tr, (NVALUES - 1)) - TRANSFER_STACK32(previous_cr0, (NVALUES - 0)) + xorl %ebx, %ebx + mov %cs, %bx + sall $4, %ebx /* %ebx is now our relocation base */ - mov physical_esp,%esi /* to be used in 32bit code */ + /* + * Load the descriptor table pointer. We'll need it when running + * in 16-bit protected mode. + */ + lgdtl bootgdtdesc - wakeup_start /* Enable protected mode */ - movl %cr0,%eax - orl $(CR0_PE),%eax - movl %eax,%cr0 + movl $CR0_PE, %eax + mov %eax, %cr0 + /* + * Now execute a far jump to turn on protected mode. This + * causes the segment registers to turn into selectors and causes + * %cs to be loaded from the gdt. + * + * The following instruction is: + * ljmpl $bootcode32 - bootgdt, $wakeup_32 - wakeup_start + * but gas cannot assemble that. And besides, we patch the targets + * in early startup and its a little clearer what we are patching. + */ wakeup_sw32: - /* Switch to protected mode by intersegmental jump */ - ljmpl $KCSEL,$0x12345678 /* Code location, to be replaced */ + .byte 0x66 /* size override to 32 bits */ + .byte 0xea /* opcode for far jump */ + .long wakeup_32 - wakeup_start /* offset in segment */ + .word bootcode32 - bootgdt /* index in gdt for 32 bit code */ /* - * Now switched to protected mode without paging enabled. - * %esi: KERNEL stack pointer (physical address) + * At this point, we are running in 32 bit legacy protected mode. */ + ALIGN_TEXT .code32 wakeup_32: - nop - /* Set up segment registers for protected mode */ - movw $KDSEL,%ax /* KDSEL to segment registers */ - movw %ax,%ds - movw %ax,%es - movw %ax,%gs - movw %ax,%ss - movw $KPSEL,%ax /* KPSEL to %fs */ - movw %ax,%fs - movl %esi,%esp /* physical address stack pointer */ - -wakeup_32stack: - /* Operands are overwritten in 16 bit code by TRANSFER_STACK32 macro */ - pushl $0xabcdef09 /* ss + dummy */ - pushl $0xabcdef08 /* fs + gs */ - pushl $0xabcdef07 /* ds + es */ - pushl $0xabcdef06 /* gdt:base (physical address) */ - pushl $0xabcdef05 /* recover address */ - pushl $0xabcdef04 /* idt:base */ - pushl $0xabcdef03 /* ldt + idt:limit */ - pushl $0xabcdef02 /* gdt:base */ - pushl $0xabcdef01 /* TR + gdt:limit */ - pushl $0xabcdef00 /* CR0 */ - - movl %esp,%ebp -#define CR0_REGISTER 0(%ebp) -#define TASK_REGISTER 4(%ebp) -#define PREVIOUS_GDT 6(%ebp) -#define PREVIOUS_LDT 12(%ebp) -#define PREVIOUS_IDT 14(%ebp) -#define RECOVER_ADDR 20(%ebp) -#define PHYSICAL_GDT_BASE 24(%ebp) -#define PREVIOUS_DS 28(%ebp) -#define PREVIOUS_ES 30(%ebp) -#define PREVIOUS_FS 32(%ebp) -#define PREVIOUS_GS 34(%ebp) -#define PREVIOUS_SS 36(%ebp) - - /* Fixup TSS type field */ -#define TSS_TYPEFIX_MASK 0xf9 - xorl %esi,%esi - movl PHYSICAL_GDT_BASE,%ebx - movw TASK_REGISTER,%si - leal (%ebx,%esi),%eax /* get TSS segment descriptor */ - andb $TSS_TYPEFIX_MASK,5(%eax) - - /* Prepare to return to sleep/wakeup code point */ - lgdtl PREVIOUS_GDT - lidtl PREVIOUS_IDT - - /* Pack values from the GDT to be loaded into segment registers. */ - movl PREVIOUS_DS,%ebx - movl PREVIOUS_FS,%ecx - movl PREVIOUS_SS,%edx - movw TASK_REGISTER,%si - shll $16,%esi - movw PREVIOUS_LDT,%si - movl RECOVER_ADDR,%edi - - /* Enable paging and etc. */ - movl CR0_REGISTER,%eax - movl %eax,%cr0 + mov $bootdata32 - bootgdt, %eax + mov %ax, %ds - /* Flush the prefetch queue */ - jmp 1f -1: jmp 1f -1: + /* Get PCB and return address. */ + movl wakeup_pcb - wakeup_start(%ebx), %esi + movl wakeup_ret - wakeup_start(%ebx), %edi + + /* Restore CR4 and CR3. */ + movl wakeup_cr4 - wakeup_start(%ebx), %eax + mov %eax, %cr4 + movl wakeup_cr3 - wakeup_start(%ebx), %eax + mov %eax, %cr3 /* - * Now we are in kernel virtual memory addressing with the following - * original register values: - * %ebx: ds + es - * %ecx: fs + gs - * %edx: ss + dummy - * %esi: LDTR + TR - * %edi: recover address - * We'll load these back into the segment registers now. + * Finally, switch to long bit mode by enabling paging. We have + * to be very careful here because all the segmentation disappears + * out from underneath us. The spec says we can depend on the + * subsequent pipelined branch to execute, but *only if* everthing + * is still identity mapped. If any mappings change, the pipeline + * will flush. */ - nop - - movl %esi,%eax /* LDTR + TR */ - lldt %ax /* load LDT register */ - shrl $16,%eax - ltr %ax /* load task register */ - - /* Restore segment registers */ - movl %ebx,%eax /* ds + es */ - movw %ax,%ds - shrl $16,%eax - movw %ax,%es - movl %ecx,%eax /* fs + gs */ - movw %ax,%fs - shrl $16,%eax - movw %ax,%gs - movl %edx,%eax /* ss */ - movw %ax,%ss + mov %cr0, %eax + orl $CR0_PG, %eax + mov %eax, %cr0 - /* Jump to acpi_restorecpu() */ + jmp 1f +1: + /* Jump to return address. */ jmp *%edi -/* used in real mode */ -physical_gdt: .word 0 - .long 0 -physical_esp: .long 0 -previous_cr2: .long 0 -previous_cr3: .long 0 -previous_cr4: .long 0 -resume_beep: .long 0 -reset_video: .long 0 + .data -/* - * Transfer from real mode to protected mode. The order of these variables - * is very important, DO NOT INSERT OR CHANGE unless you know why. - */ -previous_cr0: .long 0 -previous_tr: .word 0 -previous_gdt: .word 0 - .long 0 -previous_ldt: .word 0 -previous_idt: .word 0 - .long 0 -where_to_recover: .long 0 -previous_ds: .word 0 -previous_es: .word 0 -previous_fs: .word 0 -previous_gs: .word 0 -previous_ss: .word 0 -dummy: .word 0 +resume_beep: + .byte 0 +reset_video: + .byte 0 + + ALIGN_DATA +bootgdt: + .long 0x00000000 + .long 0x00000000 + +bootcode32: + .long 0x0000ffff + .long 0x00cf9b00 + +bootdata32: + .long 0x0000ffff + .long 0x00cf9300 +bootgdtend: + +bootgdtdesc: + .word bootgdtend - bootgdt /* Length */ + .long bootgdt - wakeup_start /* Offset plus %ds << 4 */ + + ALIGN_DATA +wakeup_cr4: + .long 0 +wakeup_cr3: + .long 0 +wakeup_pcb: + .long 0 +wakeup_ret: + .long 0 +dummy: Modified: head/sys/i386/acpica/acpi_wakeup.c ============================================================================== --- head/sys/i386/acpica/acpi_wakeup.c Fri May 18 18:53:28 2012 (r235621) +++ head/sys/i386/acpica/acpi_wakeup.c Fri May 18 18:55:58 2012 (r235622) @@ -1,6 +1,8 @@ /*- * Copyright (c) 2001 Takanori Watanabe - * Copyright (c) 2001 Mitsuru IWASAKI + * Copyright (c) 2001-2012 Mitsuru IWASAKI + * Copyright (c) 2003 Peter Wemm + * Copyright (c) 2008-2012 Jung-uk Kim * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -29,26 +31,29 @@ __FBSDID("$FreeBSD$"); #include -#include -#include #include -#include +#include +#include #include #include -#include -#include +#include #include #include -#include -#include -#include -#include -#include +#include #include #include -#include +#include +#include +#include +#include + +#ifdef SMP +#include +#include +#include +#endif #include @@ -57,164 +62,186 @@ __FBSDID("$FreeBSD$"); #include "acpi_wakecode.h" #include "acpi_wakedata.h" -/* Make sure the code is less than one page and leave room for the stack. */ +/* Make sure the code is less than a page and leave room for the stack. */ CTASSERT(sizeof(wakecode) < PAGE_SIZE - 1024); -#ifndef _SYS_CDEFS_H_ -#error this file needs sys/cdefs.h as a prerequisite +extern int acpi_resume_beep; +extern int acpi_reset_video; + +#ifdef SMP +extern struct pcb **susppcbs; +#else +static struct pcb **susppcbs; +#endif + +static void *acpi_alloc_wakeup_handler(void); +static void acpi_stop_beep(void *); + +#ifdef SMP +static int acpi_wakeup_ap(struct acpi_softc *, int); +static void acpi_wakeup_cpus(struct acpi_softc *, const cpuset_t *); #endif -extern uint32_t acpi_resume_beep; -extern uint32_t acpi_reset_video; -extern void initializecpu(void); - -static struct region_descriptor __used saved_idt, saved_gdt; -static struct region_descriptor *p_gdt; -static uint16_t __used saved_ldt; - -static uint32_t __used r_eax, r_ebx, r_ecx, r_edx, r_ebp, r_esi, r_edi, - r_efl, r_cr0, r_cr2, r_cr3, r_cr4, ret_addr; - -static uint16_t __used r_cs, r_ds, r_es, r_fs, r_gs, r_ss, r_tr; -static uint32_t __used r_esp; - -static void acpi_printcpu(void); -static void acpi_realmodeinst(void *arg, bus_dma_segment_t *segs, - int nsegs, int error); -static void acpi_alloc_wakeup_handler(void); - -/* XXX shut gcc up */ -extern int acpi_savecpu(void); -extern int acpi_restorecpu(void); - -#ifdef __GNUCLIKE_ASM -__asm__(" \n\ - .text \n\ - .p2align 2, 0x90 \n\ - .type acpi_restorecpu, @function\n\ -acpi_restorecpu: \n\ - .align 4 \n\ - movl r_eax,%eax \n\ - movl r_ebx,%ebx \n\ - movl r_ecx,%ecx \n\ - movl r_edx,%edx \n\ - movl r_ebp,%ebp \n\ - movl r_esi,%esi \n\ - movl r_edi,%edi \n\ - movl r_esp,%esp \n\ - \n\ - pushl r_efl \n\ - popfl \n\ - \n\ - movl ret_addr,%eax \n\ - movl %eax,(%esp) \n\ - xorl %eax,%eax \n\ - ret \n\ - \n\ - .text \n\ - .p2align 2, 0x90 \n\ - .type acpi_savecpu, @function \n\ -acpi_savecpu: \n\ - movw %cs,r_cs \n\ - movw %ds,r_ds \n\ - movw %es,r_es \n\ - movw %fs,r_fs \n\ - movw %gs,r_gs \n\ - movw %ss,r_ss \n\ - \n\ - movl %eax,r_eax \n\ - movl %ebx,r_ebx \n\ - movl %ecx,r_ecx \n\ - movl %edx,r_edx \n\ - movl %ebp,r_ebp \n\ - movl %esi,r_esi \n\ - movl %edi,r_edi \n\ - \n\ - movl %cr0,%eax \n\ - movl %eax,r_cr0 \n\ - movl %cr2,%eax \n\ - movl %eax,r_cr2 \n\ - movl %cr3,%eax \n\ - movl %eax,r_cr3 \n\ - movl %cr4,%eax \n\ - movl %eax,r_cr4 \n\ - \n\ - pushfl \n\ - popl r_efl \n\ - \n\ - movl %esp,r_esp \n\ - \n\ - sgdt saved_gdt \n\ - sidt saved_idt \n\ - sldt saved_ldt \n\ - str r_tr \n\ - \n\ - movl (%esp),%eax \n\ - movl %eax,ret_addr \n\ - movl $1,%eax \n\ - ret \n\ -"); -#endif /* __GNUCLIKE_ASM */ +#define ACPI_PAGETABLES 0 +#define WAKECODE_VADDR(sc) ((sc)->acpi_wakeaddr + (ACPI_PAGETABLES * PAGE_SIZE)) +#define WAKECODE_PADDR(sc) ((sc)->acpi_wakephys + (ACPI_PAGETABLES * PAGE_SIZE)) +#define WAKECODE_FIXUP(offset, type, val) do { \ + type *addr; \ + addr = (type *)(WAKECODE_VADDR(sc) + offset); \ + *addr = val; \ +} while (0) static void -acpi_printcpu(void) +acpi_stop_beep(void *arg) { - printf("======== acpi_printcpu() debug dump ========\n"); - printf("gdt[%04x:%08x] idt[%04x:%08x] ldt[%04x] tr[%04x] efl[%08x]\n", - saved_gdt.rd_limit, saved_gdt.rd_base, - saved_idt.rd_limit, saved_idt.rd_base, - saved_ldt, r_tr, r_efl); - printf("eax[%08x] ebx[%08x] ecx[%08x] edx[%08x]\n", - r_eax, r_ebx, r_ecx, r_edx); - printf("esi[%08x] edi[%08x] ebp[%08x] esp[%08x]\n", - r_esi, r_edi, r_ebp, r_esp); - printf("cr0[%08x] cr2[%08x] cr3[%08x] cr4[%08x]\n", - r_cr0, r_cr2, r_cr3, r_cr4); - printf("cs[%04x] ds[%04x] es[%04x] fs[%04x] gs[%04x] ss[%04x]\n", - r_cs, r_ds, r_es, r_fs, r_gs, r_ss); + + if (acpi_resume_beep != 0) + timer_spkr_release(); } -#define WAKECODE_FIXUP(offset, type, val) do { \ - type *addr; \ - addr = (type *)(sc->acpi_wakeaddr + offset); \ - *addr = val; \ -} while (0) +#ifdef SMP +static int +acpi_wakeup_ap(struct acpi_softc *sc, int cpu) +{ + int vector = (WAKECODE_PADDR(sc) >> 12) & 0xff; + int apic_id = cpu_apic_ids[cpu]; + int ms; + + WAKECODE_FIXUP(wakeup_pcb, struct pcb *, susppcbs[cpu]); + + /* do an INIT IPI: assert RESET */ + lapic_ipi_raw(APIC_DEST_DESTFLD | APIC_TRIGMOD_EDGE | + APIC_LEVEL_ASSERT | APIC_DESTMODE_PHY | APIC_DELMODE_INIT, apic_id); + + /* wait for pending status end */ + lapic_ipi_wait(-1); + + /* do an INIT IPI: deassert RESET */ + lapic_ipi_raw(APIC_DEST_ALLESELF | APIC_TRIGMOD_LEVEL | + APIC_LEVEL_DEASSERT | APIC_DESTMODE_PHY | APIC_DELMODE_INIT, 0); + + /* wait for pending status end */ + DELAY(10000); /* wait ~10mS */ + lapic_ipi_wait(-1); -#define WAKECODE_BCOPY(offset, type, val) do { \ - void *addr; \ - addr = (void *)(sc->acpi_wakeaddr + offset); \ - bcopy(&(val), addr, sizeof(type)); \ -} while (0) + /* + * next we do a STARTUP IPI: the previous INIT IPI might still be + * latched, (P5 bug) this 1st STARTUP would then terminate + * immediately, and the previously started INIT IPI would continue. OR + * the previous INIT IPI has already run. and this STARTUP IPI will + * run. OR the previous INIT IPI was ignored. and this STARTUP IPI + * will run. + */ + + /* do a STARTUP IPI */ + lapic_ipi_raw(APIC_DEST_DESTFLD | APIC_TRIGMOD_EDGE | + APIC_LEVEL_DEASSERT | APIC_DESTMODE_PHY | APIC_DELMODE_STARTUP | + vector, apic_id); + lapic_ipi_wait(-1); + DELAY(200); /* wait ~200uS */ + + /* + * finally we do a 2nd STARTUP IPI: this 2nd STARTUP IPI should run IF + * the previous STARTUP IPI was cancelled by a latched INIT IPI. OR + * this STARTUP IPI will be ignored, as only ONE STARTUP IPI is + * recognized after hardware RESET or INIT IPI. + */ + + lapic_ipi_raw(APIC_DEST_DESTFLD | APIC_TRIGMOD_EDGE | + APIC_LEVEL_DEASSERT | APIC_DESTMODE_PHY | APIC_DELMODE_STARTUP | + vector, apic_id); + lapic_ipi_wait(-1); + DELAY(200); /* wait ~200uS */ + + /* Wait up to 5 seconds for it to start. */ + for (ms = 0; ms < 5000; ms++) { + if (susppcbs[cpu]->pcb_eip == 0) + return (1); /* return SUCCESS */ + DELAY(1000); + } + return (0); /* return FAILURE */ +} + +#define WARMBOOT_TARGET 0 +#define WARMBOOT_OFF (KERNBASE + 0x0467) +#define WARMBOOT_SEG (KERNBASE + 0x0469) + +#define CMOS_REG (0x70) +#define CMOS_DATA (0x71) +#define BIOS_RESET (0x0f) +#define BIOS_WARM (0x0a) -/* Turn off bits 1&2 of the PIT, stopping the beep. */ static void -acpi_stop_beep(void *arg) +acpi_wakeup_cpus(struct acpi_softc *sc, const cpuset_t *wakeup_cpus) { - outb(0x61, inb(0x61) & ~0x3); + uint32_t mpbioswarmvec; + int cpu; + u_char mpbiosreason; + + /* save the current value of the warm-start vector */ + mpbioswarmvec = *((uint32_t *)WARMBOOT_OFF); + outb(CMOS_REG, BIOS_RESET); + mpbiosreason = inb(CMOS_DATA); + + /* setup a vector to our boot code */ + *((volatile u_short *)WARMBOOT_OFF) = WARMBOOT_TARGET; + *((volatile u_short *)WARMBOOT_SEG) = WAKECODE_PADDR(sc) >> 4; + outb(CMOS_REG, BIOS_RESET); + outb(CMOS_DATA, BIOS_WARM); /* 'warm-start' */ + + /* Wake up each AP. */ + for (cpu = 1; cpu < mp_ncpus; cpu++) { + if (!CPU_ISSET(cpu, wakeup_cpus)) + continue; + if (acpi_wakeup_ap(sc, cpu) == 0) { + /* restore the warmstart vector */ + *(uint32_t *)WARMBOOT_OFF = mpbioswarmvec; + panic("acpi_wakeup: failed to resume AP #%d (PHY #%d)", + cpu, cpu_apic_ids[cpu]); + } + } + + /* restore the warmstart vector */ + *(uint32_t *)WARMBOOT_OFF = mpbioswarmvec; + + outb(CMOS_REG, BIOS_RESET); + outb(CMOS_DATA, mpbiosreason); } +#endif int acpi_sleep_machdep(struct acpi_softc *sc, int state) { - ACPI_STATUS status; - struct pmap *pm; - int ret; - uint32_t cr3; - u_long ef; +#ifdef SMP + cpuset_t wakeup_cpus; +#endif + register_t cr3, rf; + ACPI_STATUS status; + struct pmap *pm; + int ret; ret = -1; - if (sc->acpi_wakeaddr == 0) + + if (sc->acpi_wakeaddr == 0ul) return (ret); - AcpiSetFirmwareWakingVector(sc->acpi_wakephys); +#ifdef SMP + wakeup_cpus = all_cpus; + CPU_CLR(PCPU_GET(cpuid), &wakeup_cpus); +#endif + + if (acpi_resume_beep != 0) + timer_spkr_acquire(); - ef = intr_disable(); + AcpiSetFirmwareWakingVector(WAKECODE_PADDR(sc)); + + rf = intr_disable(); intr_suspend(); /* - * Temporarily switch to the kernel pmap because it provides an - * identity mapping (setup at boot) for the low physical memory - * region containing the wakeup code. + * Temporarily switch to the kernel pmap because it provides + * an identity mapping (setup at boot) for the low physical + * memory region containing the wakeup code. */ pm = kernel_pmap; cr3 = rcr3(); @@ -224,39 +251,22 @@ acpi_sleep_machdep(struct acpi_softc *sc load_cr3(vtophys(pm->pm_pdir)); #endif - ret_addr = 0; - if (acpi_savecpu()) { - /* Execute Sleep */ - - p_gdt = (struct region_descriptor *) - (sc->acpi_wakeaddr + physical_gdt); - p_gdt->rd_limit = saved_gdt.rd_limit; - p_gdt->rd_base = vtophys(saved_gdt.rd_base); - - WAKECODE_FIXUP(physical_esp, uint32_t, vtophys(r_esp)); - WAKECODE_FIXUP(previous_cr0, uint32_t, r_cr0); - WAKECODE_FIXUP(previous_cr2, uint32_t, r_cr2); - WAKECODE_FIXUP(previous_cr3, uint32_t, r_cr3); - WAKECODE_FIXUP(previous_cr4, uint32_t, r_cr4); - - WAKECODE_FIXUP(resume_beep, uint32_t, acpi_resume_beep); - WAKECODE_FIXUP(reset_video, uint32_t, acpi_reset_video); - - WAKECODE_FIXUP(previous_tr, uint16_t, r_tr); - WAKECODE_BCOPY(previous_gdt, struct region_descriptor, saved_gdt); - WAKECODE_FIXUP(previous_ldt, uint16_t, saved_ldt); - WAKECODE_BCOPY(previous_idt, struct region_descriptor, saved_idt); - - WAKECODE_FIXUP(where_to_recover, void *, acpi_restorecpu); - - WAKECODE_FIXUP(previous_ds, uint16_t, r_ds); - WAKECODE_FIXUP(previous_es, uint16_t, r_es); - WAKECODE_FIXUP(previous_fs, uint16_t, r_fs); - WAKECODE_FIXUP(previous_gs, uint16_t, r_gs); - WAKECODE_FIXUP(previous_ss, uint16_t, r_ss); + if (suspendctx(susppcbs[0])) { +#ifdef SMP + if (!CPU_EMPTY(&wakeup_cpus) && + suspend_cpus(wakeup_cpus) == 0) { + device_printf(sc->acpi_dev, "Failed to suspend APs\n"); + goto out; + } +#endif + + WAKECODE_FIXUP(resume_beep, uint8_t, (acpi_resume_beep != 0)); + WAKECODE_FIXUP(reset_video, uint8_t, (acpi_reset_video != 0)); + + WAKECODE_FIXUP(wakeup_cr4, register_t, susppcbs[0]->pcb_cr4); + WAKECODE_FIXUP(wakeup_cr3, register_t, susppcbs[0]->pcb_cr3); - if (bootverbose) - acpi_printcpu(); + WAKECODE_FIXUP(wakeup_pcb, struct pcb *, susppcbs[0]); /* Call ACPICA to enter the desired sleep state */ if (state == ACPI_STATE_S4 && sc->acpi_s4bios) @@ -266,8 +276,8 @@ acpi_sleep_machdep(struct acpi_softc *sc if (status != AE_OK) { device_printf(sc->acpi_dev, - "AcpiEnterSleepState failed - %s\n", - AcpiFormatException(status)); + "AcpiEnterSleepState failed - %s\n", + AcpiFormatException(status)); goto out; } @@ -275,97 +285,96 @@ acpi_sleep_machdep(struct acpi_softc *sc ia32_pause(); } else { pmap_init_pat(); + initializecpu(); PCPU_SET(switchtime, 0); PCPU_SET(switchticks, ticks); - if (bootverbose) { - acpi_savecpu(); - acpi_printcpu(); - } +#ifdef SMP + if (!CPU_EMPTY(&wakeup_cpus)) + acpi_wakeup_cpus(sc, &wakeup_cpus); +#endif ret = 0; } out: +#ifdef SMP + if (!CPU_EMPTY(&wakeup_cpus)) + restart_cpus(wakeup_cpus); +#endif + load_cr3(cr3); mca_resume(); intr_resume(); - intr_restore(ef); + intr_restore(rf); + + AcpiSetFirmwareWakingVector(0); if (ret == 0 && mem_range_softc.mr_op != NULL && mem_range_softc.mr_op->reinit != NULL) mem_range_softc.mr_op->reinit(&mem_range_softc); - /* If we beeped, turn it off after a delay. */ - if (acpi_resume_beep) - timeout(acpi_stop_beep, NULL, 3 * hz); - return (ret); } -static bus_dma_tag_t acpi_waketag; -static bus_dmamap_t acpi_wakemap; -static vm_offset_t acpi_wakeaddr; - -static void +static void * acpi_alloc_wakeup_handler(void) { - void *wakeaddr; - - if (!cold) - return; + void *wakeaddr; + int i; /* * Specify the region for our wakeup code. We want it in the low 1 MB - * region, excluding video memory and above (0xa0000). We ask for - * it to be page-aligned, just to be safe. + * region, excluding real mode IVT (0-0x3ff), BDA (0x400-0x4ff), EBDA + * (less than 128KB, below 0xa0000, must be excluded by SMAP and DSDT), + * and ROM area (0xa0000 and above). The temporary page tables must be + * page-aligned. */ - if (bus_dma_tag_create(/*parent*/ NULL, - /*alignment*/ PAGE_SIZE, /*no boundary*/ 0, - /*lowaddr*/ 0x9ffff, /*highaddr*/ BUS_SPACE_MAXADDR, NULL, NULL, - /*maxsize*/ PAGE_SIZE, /*segments*/ 1, /*maxsegsize*/ PAGE_SIZE, - 0, busdma_lock_mutex, &Giant, &acpi_waketag) != 0) { - printf("acpi_alloc_wakeup_handler: can't create wake tag\n"); - return; + wakeaddr = contigmalloc((ACPI_PAGETABLES + 1) * PAGE_SIZE, M_DEVBUF, + M_NOWAIT, 0x500, 0xa0000, PAGE_SIZE, 0ul); + if (wakeaddr == NULL) { + printf("%s: can't alloc wake memory\n", __func__); + return (NULL); } - if (bus_dmamem_alloc(acpi_waketag, &wakeaddr, BUS_DMA_NOWAIT, - &acpi_wakemap) != 0) { - printf("acpi_alloc_wakeup_handler: can't alloc wake memory\n"); - return; + if (EVENTHANDLER_REGISTER(power_resume, acpi_stop_beep, NULL, + EVENTHANDLER_PRI_LAST) == NULL) { + printf("%s: can't register event handler\n", __func__); + contigfree(wakeaddr, (ACPI_PAGETABLES + 1) * PAGE_SIZE, M_DEVBUF); + return (NULL); } - acpi_wakeaddr = (vm_offset_t)wakeaddr; -} - -SYSINIT(acpiwakeup, SI_SUB_KMEM, SI_ORDER_ANY, acpi_alloc_wakeup_handler, 0); - -static void -acpi_realmodeinst(void *arg, bus_dma_segment_t *segs, int nsegs, int error) -{ - struct acpi_softc *sc; - uint32_t *addr; - - /* Overwrite the ljmp target with the real address */ - sc = arg; - sc->acpi_wakephys = segs[0].ds_addr; - addr = (uint32_t *)&wakecode[wakeup_sw32 + 2]; - *addr = sc->acpi_wakephys + wakeup_32; - - /* Copy the wake code into our low page and save its physical addr. */ - bcopy(wakecode, (void *)sc->acpi_wakeaddr, sizeof(wakecode)); - if (bootverbose) { - device_printf(sc->acpi_dev, "wakeup code va %#x pa %#jx\n", - acpi_wakeaddr, (uintmax_t)sc->acpi_wakephys); + susppcbs = malloc(mp_ncpus * sizeof(*susppcbs), M_DEVBUF, M_WAITOK); + for (i = 0; i < mp_ncpus; i++) { + susppcbs[i] = malloc(sizeof(**susppcbs), M_DEVBUF, M_WAITOK); } + + return (wakeaddr); } void acpi_install_wakeup_handler(struct acpi_softc *sc) { - if (acpi_wakeaddr == 0) + static void *wakeaddr = NULL; + + if (wakeaddr != NULL) return; - sc->acpi_waketag = acpi_waketag; - sc->acpi_wakeaddr = acpi_wakeaddr; - sc->acpi_wakemap = acpi_wakemap; + wakeaddr = acpi_alloc_wakeup_handler(); + if (wakeaddr == NULL) + return; + + sc->acpi_wakeaddr = (vm_offset_t)wakeaddr; + sc->acpi_wakephys = vtophys(wakeaddr); + + bcopy(wakecode, (void *)WAKECODE_VADDR(sc), sizeof(wakecode)); - bus_dmamap_load(sc->acpi_waketag, sc->acpi_wakemap, - (void *)sc->acpi_wakeaddr, PAGE_SIZE, acpi_realmodeinst, sc, 0); + /* Patch GDT base address, ljmp target. */ + WAKECODE_FIXUP((bootgdtdesc + 2), uint32_t, + WAKECODE_PADDR(sc) + bootgdt); + WAKECODE_FIXUP((wakeup_sw32 + 2), uint32_t, + WAKECODE_PADDR(sc) + wakeup_32); + + /* Save pointers to some global data. */ + WAKECODE_FIXUP(wakeup_ret, void *, resumectx); + + if (bootverbose) + device_printf(sc->acpi_dev, "wakeup code va %p pa %p\n", + (void *)sc->acpi_wakeaddr, (void *)sc->acpi_wakephys); } Modified: head/sys/i386/i386/apic_vector.s ============================================================================== --- head/sys/i386/i386/apic_vector.s Fri May 18 18:53:28 2012 (r235621) +++ head/sys/i386/i386/apic_vector.s Fri May 18 18:55:58 2012 (r235622) @@ -334,6 +334,24 @@ IDTVEC(cpustop) iret /* + * Executed by a CPU when it receives an IPI_SUSPEND from another CPU. + */ + .text + SUPERALIGN_TEXT +IDTVEC(cpususpend) + PUSH_FRAME + SET_KERNEL_SREGS + cld + + movl lapic, %eax + movl $0, LA_EOI(%eax) /* End Of Interrupt to APIC */ + + call cpususpend_handler + + POP_FRAME + jmp doreti_iret + +/* * Executed by a CPU when it receives a RENDEZVOUS IPI from another CPU. * * - Calls the generic rendezvous action function. Modified: head/sys/i386/i386/genassym.c ============================================================================== --- head/sys/i386/i386/genassym.c Fri May 18 18:53:28 2012 (r235621) +++ head/sys/i386/i386/genassym.c Fri May 18 18:55:58 2012 (r235622) @@ -121,7 +121,10 @@ ASSYM(VM_MAXUSER_ADDRESS, VM_MAXUSER_ADD ASSYM(KERNBASE, KERNBASE); ASSYM(KERNLOAD, KERNLOAD); ASSYM(MCLBYTES, MCLBYTES); *** DIFF OUTPUT TRUNCATED AT 1000 LINES ***