From b861f9f3fbe15a4432a4d54b195d2c85b1ee596c Mon Sep 17 00:00:00 2001 From: Mike Larkin Date: Tue, 20 Jan 2009 20:21:04 +0000 Subject: Install ACPI S3 resume trampoline code in a lowmem page. First part of ACPI S3 suspend/resume support. This is for i386. Help/comments from art, toby, marco, jordan, kurt ok marco@, kurt@ --- sys/arch/i386/conf/files.i386 | 3 +- sys/arch/i386/i386/acpi_machdep.c | 23 +- sys/arch/i386/i386/acpi_wakecode.S | 482 +++++++++++++++++++++++++++++++++++++ sys/arch/i386/i386/cpu.c | 5 +- sys/arch/i386/i386/machdep.c | 24 +- sys/arch/i386/include/biosvar.h | 5 +- 6 files changed, 533 insertions(+), 9 deletions(-) create mode 100644 sys/arch/i386/i386/acpi_wakecode.S (limited to 'sys/arch/i386') diff --git a/sys/arch/i386/conf/files.i386 b/sys/arch/i386/conf/files.i386 index 89568b35eee..f805def876d 100644 --- a/sys/arch/i386/conf/files.i386 +++ b/sys/arch/i386/conf/files.i386 @@ -1,4 +1,4 @@ -# $OpenBSD: files.i386,v 1.187 2009/01/13 13:53:50 kettenis Exp $ +# $OpenBSD: files.i386,v 1.188 2009/01/20 20:21:03 mlarkin Exp $ # # new style config file for i386 architecture # @@ -426,6 +426,7 @@ include "dev/bluetooth/files.bluetooth" include "../../../dev/acpi/files.acpi" file arch/i386/i386/acpi_machdep.c acpi +file arch/i386/i386/acpi_wakecode.S acpi # # IPMI diff --git a/sys/arch/i386/i386/acpi_machdep.c b/sys/arch/i386/i386/acpi_machdep.c index e4244315dd8..8f5f7b36b87 100644 --- a/sys/arch/i386/i386/acpi_machdep.c +++ b/sys/arch/i386/i386/acpi_machdep.c @@ -1,4 +1,4 @@ -/* $OpenBSD: acpi_machdep.c,v 1.15 2008/12/28 22:27:10 kettenis Exp $ */ +/* $OpenBSD: acpi_machdep.c,v 1.16 2009/01/20 20:21:03 mlarkin Exp $ */ /* * Copyright (c) 2005 Thorsten Lockert * @@ -28,6 +28,9 @@ #include #include +#include +#include + #include #include #include @@ -38,6 +41,10 @@ int haveacpibutusingapm; #endif +extern u_char acpi_real_mode_resume[], acpi_resume_end[]; + +int acpi_savecpu(void); + #define ACPI_BIOS_RSDP_WINDOW_BASE 0xe0000 #define ACPI_BIOS_RSDP_WINDOW_SIZE 0x20000 @@ -165,5 +172,17 @@ acpi_attach_machdep(struct acpi_softc *sc) acpiapm_ioctl = acpiioctl; acpiapm_kqfilter = acpikqfilter; cpuresetfn = acpi_reset; + +#ifdef ACPI_SLEEP_ENABLED + + /* + * Sanity check before setting up trampoline. + * Ensure the trampoline size is < PAGE_SIZE + */ + KASSERT(acpi_resume_end - acpi_real_mode_resume < PAGE_SIZE); + + bcopy(acpi_real_mode_resume, (caddr_t)ACPI_TRAMPOLINE, acpi_resume_end - acpi_real_mode_resume); +#endif /* ACPI_SLEEP_ENABLED */ + } -#endif /* SMALL_KERNEL */ +#endif /* ! SMALL_KERNEL */ diff --git a/sys/arch/i386/i386/acpi_wakecode.S b/sys/arch/i386/i386/acpi_wakecode.S new file mode 100644 index 00000000000..daedfdf7a07 --- /dev/null +++ b/sys/arch/i386/i386/acpi_wakecode.S @@ -0,0 +1,482 @@ +/* + * Copyright (c) 2001 Takanori Watanabe + * Copyright (c) 2001 Mitsuru IWASAKI + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +/* + * Copyright (c) 2008 Mike Larkin + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "acpi.h" + +#if NACPI > 0 +#ifndef SMALL_KERNEL + +#define _ACPI_WAKECODE + +#include "assym.h" +#include +#include +#include +#include +#include + +#define _ACPI_TRMP_LABEL(a) a = . - _C_LABEL(acpi_real_mode_resume) + ACPI_TRAMPOLINE +#define _ACPI_TRMP_OFFSET(a) a = . - _C_LABEL(acpi_real_mode_resume) + +/* + * On wakeup, we'll start executing at acpi_real_mode_resume. + * This is based on the wakeup vector previously stored with + * ACPI before we went to sleep. ACPI's wakeup vector is a + * physical address - in our case, it's calculated and mapped + * by the kernel and stuffed into a low page early in the boot + * process. + * + * We wakeup in real mode, at some phys addr based on the ACPI + * specification (cs = phys>>8, ip = phys & 0xF). For example, + * if our phys addr is 0x4000, we'd have cs=0x0400,ip=0 + * + * The wakeup code needs to do the following: + * 1. Reenable the video display + * 2. Enter 32 bit protected mode + * 3. Reenable paging + * 4. Restore saved CPU registers + */ + + .text + .code16 + .align 4,0 + .global _C_LABEL(acpi_real_mode_resume) + .global _C_LABEL(acpi_protected_mode_resume) + .global _C_LABEL(acpi_resume_end) +_C_LABEL(acpi_real_mode_resume): + nop + cli + cld + + /* + * Set up segment registers for real mode. + * We'll only be in real mode for a moment, and we don't have + * ant real dependencies on data or stack, so we'll just use + * the code segment for data and stack (eg, a 64k memory space). + */ + movw %cs,%ax + movw %ax,%ds + movw %ax,%ss + + /* + * Set up stack to grow down from offset 0x0FFE. + * We will only be doing a few push/pops and no calls in real + * mode, so as long as the real mode code in the segment + * plus stack doesn't exceed 0x0FFE (4094) bytes, we'll be ok. + */ + movw $0x0FFE,%sp + + /* + * Clear flags + */ + pushl $0 + popfl + + /* + * Reset the video hardware (as best as we can). + * We call the video bios at c000:0003, similar to + * what the BIOS does on a machine restart. + * Note that this will only reset the video card, + * and may not enable LCDs or other attached displays. + * + * This will also put the hardware in "factory default" + * display mode, which may not match what we had + * when we went to sleep. On many machines (specifically + * laptops), we might not restore the proper VGA mode + * on resume. Caveat emptor. + */ + lcall $0xc000,$3 + + /* + * Restore our segment registers in case the call to + * reset the video hardware clobbered them. + */ + movw %cs,%ax + movw %ax,%ds + movw %ax,%ss + + /* + * Set up esi to point to start of current routine's CS. + */ + xorl %esi,%esi + movw %cs,%si + shll $4,%esi + + /* + * Flush instruction prefetch queue + */ + jmp 1f +1: jmp 1f +1: + + + /* + * We're about to enter protected mode, so we need a GDT for that. + * Set up a temporary GDT describing 2 segments, one for code + * extending from 0x00000000-0xffffffff and one for data + * with the same range. This GDT will only be in use for a short + * time, until we restore the saved GDT that we had when we went + * to sleep (although on i386, the saved GDT will most likely + * represent something similar based on machine/segment.h). + */ + data32 addr32 lgdt tmp_gdt + + /* + * Enable protected mode by setting the PE bit in CR0 + */ + mov %cr0,%eax + orl $(CR0_PE),%eax + mov %eax,%cr0 + + /* + * Force CPU into protected mode + * by making an intersegment jump (to ourselves, just a few lines + * down from here. We rely on the kernel to fixup the jump + * target addres previously. + * + */ + ljmpl $0x8, $acpi_protected_mode_trampoline + +_ACPI_TRMP_LABEL(acpi_protected_mode_trampoline) +_C_LABEL(acpi_protected_mode_resume): + .code32 + .align 16 + + nop + + /* + * We're in protected mode now, without paging enabled. + * + * Set up segment selectors for protected mode. + * We've already set up our cs via the intersegment jump earlier, + * but we need to set ds,es,fs,gs,ss to all point to the + * 4GB flat data segment we defined earlier. + */ + movw $GSEL(GDATA_SEL,SEL_KPL),%ax + movw %ax,%ds + movw %ax,%es + movw %ax,%gs + movw %ax,%ss + movw %ax,%fs + + /* + * Reset ESP based on protected mode. We can do this here + * because we haven't put anything on the stack via a + * call or push that we haven't cleaned up already. + */ + movl %esi, %esp + addl $0x0FFE, %esp + + /* + * Shortly, we'll restore the TSS for the task that was running + * immediately before suspend occured. Since that task was the + * running task, it's TSS busy flag will have been set. We need + * to clear that bit (since we're effectively "restarting" the OS) + * in order to convince the processor that the task is no longer + * running (which is true, now). If we don't do this, when the + * OS resumes and resumes this task, it will assume we're trying + * to recurse into an already active task, which would cause + * a GP violation (and probably, a crash). + * + * We accomplish this by changing the TSS descriptor from + * BUSY (0x0B) to AVAILABLE (0x09). We keep the other + * high 4 bits intact. + */ + movl acpi_saved_gdt+2,%ebx + movzxw acpi_saved_tr,%ecx + leal (%ebx,%ecx),%eax + andb $0xF9,5(%eax) + + /* + * Reset our page size extension (via restoring cr4) to what + * it was before we suspended. If we don't do this, cr4 might + * contain garbage in the PSE bit, leading to pages that + * are incorrectly interpreted as the wrong size + * CR4 was added in i586, so there is + * an implicit assumption here that this code will execute on + * i586 or later. + */ + mov acpi_saved_cr4,%eax + mov %eax,%cr4 + + /* + * Re-enable paging, using the CR3 we stored before suspend + * as our new page table base location. Restore CR0 after + * that. + */ + movl acpi_saved_cr3,%eax + movl %eax,%cr3 + movl acpi_saved_cr0, %eax + movl %eax, %cr0 + + /* + * Flush the prefetch queue in order to enforce usage + * of the new (old) page tables we just re-enabled + */ + jmp 1f +1: jmp 1f +1: + nop + + /* + * Restore CPU segment descriptor registers + */ + lgdt acpi_saved_gdt + lidt acpi_saved_idt + lldt acpi_saved_ldt + + mov acpi_saved_cr2,%eax + mov %eax,%cr2 + + /* + * It is highly likely that the selectors we already loaded into + * these registers are already accurate, but we reload them + * again, for consistency. + */ + movw acpi_saved_es,%ax + movw %ax,%es + movw acpi_saved_fs,%ax + movw %ax,%fs + movw acpi_saved_gs,%ax + movw %ax,%gs + movw acpi_saved_ss,%ax + movw %ax,%ss + movw acpi_saved_ds,%ax + movw %ax,%ds + + /* + * Everything is almost reset back to the way it was immediately before + * suspend. There are a few more registers to restore - this is + * done in acpi_restorecpu. We'll jump to that routine, and after + * that, jump back to the OS. There's still some things + * to do there, like re-enable interrupts, resume devices, APICs, + * etc. + */ + movl acpi_saved_ebx, %ebx + movl acpi_saved_ecx, %ecx + movl acpi_saved_edx, %edx + movl acpi_saved_ebp, %ebp + movl acpi_saved_esi, %esi + movl acpi_saved_edi, %edi + movl acpi_saved_esp, %esp + push acpi_saved_fl + popfl + + /* + * XXX - The following ltr instruction occasionally causes + * A GP(0) violation. According to Intel, this can + * happen for various reasons, none of which is + * possible based on the register restore code above. + * The task register restore is thereby disabled + * until the reason for the occasional GP(0) can + * be identified. + * + * It's either a bad DS (unlikely), or the saved TR + * points to something other than a TSS (equally + * unlikely). + */ + /* ltr acpi_saved_tr */ + + /* + * Return to the OS. We've previously saved the resume + * address in acpi_saved_ret (via a call to acpi_savecpu + * before we went to sleep. + */ + xorl %eax, %eax + jmp *acpi_saved_ret + + + + .align 8 +_ACPI_TRMP_OFFSET(tmp_gdt) + .word tmp_gdt_end - tmp_gdtable + .long tmp_gdtable + + .align 8,0 +_ACPI_TRMP_LABEL(tmp_gdtable) + /* + * null + */ + .word 0, 0 + .byte 0, 0, 0, 0 + /* + * Code + * Limit: 0xffffffff + * Base: 0x00000000 + * Descriptor Type: Code + * Segment Type: CRA + * Present: True + * Priv: 0 + * AVL: False + * 64-bit: False + * 32-bit: True + * + */ + .word 0xffff, 0 + .byte 0, 0x9f, 0xcf, 0 + + /* + * Data + * Limit: 0xffffffff + * Base: 0x00000000 + * Descriptor Type: + * Segment Type: W + * Present: True + * Priv: 0 + * AVL: False + * 64-bit: False + * 32-bit: True + * + */ + .word 0xffff, 0 + .byte 0, 0x93, 0xcf, 0 + +_ACPI_TRMP_LABEL(tmp_gdt_end) + + .align 4 +_ACPI_TRMP_LABEL(acpi_saved_ebx) + .long 0 +_ACPI_TRMP_LABEL(acpi_saved_ecx) + .long 0 +_ACPI_TRMP_LABEL(acpi_saved_edx) + .long 0 +_ACPI_TRMP_LABEL(acpi_saved_ebp) + .long 0 +_ACPI_TRMP_LABEL(acpi_saved_esi) + .long 0 +_ACPI_TRMP_LABEL(acpi_saved_edi) + .long 0 +_ACPI_TRMP_LABEL(acpi_saved_esp) + .long 0 + +_ACPI_TRMP_LABEL(acpi_saved_fl) + .long 0 +_ACPI_TRMP_LABEL(acpi_saved_cr0) + .long 0 +_ACPI_TRMP_LABEL(acpi_saved_cr2) + .long 0 +_ACPI_TRMP_LABEL(acpi_saved_cr3) + .long 0 +_ACPI_TRMP_LABEL(acpi_saved_cr4) + .long 0 + +_ACPI_TRMP_LABEL(acpi_saved_ret) + .long 0 + + .align 16 +_ACPI_TRMP_LABEL(acpi_saved_idt) + .space 6 + + .align 16 +_ACPI_TRMP_LABEL(acpi_saved_gdt) + .space 6 + + .align 16 +_ACPI_TRMP_LABEL(acpi_saved_ldt) + .short 0 +_ACPI_TRMP_LABEL(acpi_saved_cs) + .short 0 +_ACPI_TRMP_LABEL(acpi_saved_ds) + .short 0 +_ACPI_TRMP_LABEL(acpi_saved_es) + .short 0 +_ACPI_TRMP_LABEL(acpi_saved_fs) + .short 0 +_ACPI_TRMP_LABEL(acpi_saved_gs) + .short 0 +_ACPI_TRMP_LABEL(acpi_saved_ss) + .short 0 +_ACPI_TRMP_LABEL(acpi_saved_tr) + .short 0 + + /* + * End of resume code (code copied to ACPI_TRAMPOLINE) + */ +_C_LABEL(acpi_resume_end): + + /* + * acpi_savecpu saves the processor's registers and flags + * for use during the ACPI suspend/resume process. + */ + +NENTRY(acpi_savecpu) + movl (%esp), %eax + movl %eax, acpi_saved_ret + + movw %cs, acpi_saved_cs + movw %ds, acpi_saved_ds + movw %es, acpi_saved_es + movw %fs, acpi_saved_fs + movw %gs, acpi_saved_gs + movw %ss, acpi_saved_ss + + movl %ebx, acpi_saved_ebx + movl %ecx, acpi_saved_ecx + movl %edx, acpi_saved_edx + movl %ebp, acpi_saved_ebp + movl %esi, acpi_saved_esi + movl %edi, acpi_saved_edi + movl %esp, acpi_saved_esp + + pushfl + popl acpi_saved_fl + + movl %cr0, %eax + movl %eax, acpi_saved_cr0 + movl %cr2, %eax + movl %eax, acpi_saved_cr2 + movl %cr3, %eax + movl %eax, acpi_saved_cr3 + movl %cr4, %eax + movl %eax, acpi_saved_cr4 + + sgdt acpi_saved_gdt + sidt acpi_saved_idt + sldt acpi_saved_ldt + str acpi_saved_tr + + movl $1, %eax + ret + + +#endif /* SMALL_KERNEL */ +#endif /* NACPI > 0 */ diff --git a/sys/arch/i386/i386/cpu.c b/sys/arch/i386/i386/cpu.c index f4ef5cd537f..fc8e0e4919c 100644 --- a/sys/arch/i386/i386/cpu.c +++ b/sys/arch/i386/i386/cpu.c @@ -1,4 +1,4 @@ -/* $OpenBSD: cpu.c,v 1.33 2008/10/19 20:48:10 brad Exp $ */ +/* $OpenBSD: cpu.c,v 1.34 2009/01/20 20:21:03 mlarkin Exp $ */ /* $NetBSD: cpu.c,v 1.1.2.7 2000/06/26 02:04:05 sommerfeld Exp $ */ /*- @@ -464,9 +464,6 @@ cpu_copy_trampoline() extern u_char cpu_spinup_trampoline[]; extern u_char cpu_spinup_trampoline_end[]; - pmap_kenter_pa((vaddr_t)MP_TRAMPOLINE, /* virtual */ - (paddr_t)MP_TRAMPOLINE, /* physical */ - VM_PROT_ALL); /* protection */ bcopy(cpu_spinup_trampoline, (caddr_t)MP_TRAMPOLINE, cpu_spinup_trampoline_end - cpu_spinup_trampoline); } diff --git a/sys/arch/i386/i386/machdep.c b/sys/arch/i386/i386/machdep.c index d81f5b4501c..1b3d3390a51 100644 --- a/sys/arch/i386/i386/machdep.c +++ b/sys/arch/i386/i386/machdep.c @@ -1,4 +1,4 @@ -/* $OpenBSD: machdep.c,v 1.443 2009/01/11 07:12:07 jsg Exp $ */ +/* $OpenBSD: machdep.c,v 1.444 2009/01/20 20:21:03 mlarkin Exp $ */ /* $NetBSD: machdep.c,v 1.214 1996/11/10 03:16:17 thorpej Exp $ */ /*- @@ -118,6 +118,9 @@ #include #include #include +#ifdef MULTIPROCESSOR +#include +#endif /* MULTIPROCESSOR */ #include #include @@ -3117,6 +3120,25 @@ init386(paddr_t first_avail) #ifdef DEBUG printf("\n"); #endif + +#if defined(MULTIPROCESSOR) || \ + (NACPI > 0 && defined(ACPI_SLEEP_ENABLED) && !defined(SMALL_KERNEL)) + /* install the lowmem ptp after boot args for 1:1 mappings */ + pmap_prealloc_lowmem_ptp(PTP0_PA); +#endif + +#ifdef MULTIPROCESSOR + pmap_kenter_pa((vaddr_t)MP_TRAMPOLINE, /* virtual */ + (paddr_t)MP_TRAMPOLINE, /* physical */ + VM_PROT_ALL); /* protection */ +#endif + +#if NACPI > 0 && defined(ACPI_SLEEP_ENABLED) && !defined(SMALL_KERNEL) + pmap_kenter_pa((vaddr_t)ACPI_TRAMPOLINE,/* virtual */ + (paddr_t)ACPI_TRAMPOLINE, /* physical */ + VM_PROT_ALL); /* protection */ +#endif + tlbflush(); #if 0 #if NISADMA > 0 diff --git a/sys/arch/i386/include/biosvar.h b/sys/arch/i386/include/biosvar.h index 4a55da90ae4..d807e1de5d2 100644 --- a/sys/arch/i386/include/biosvar.h +++ b/sys/arch/i386/include/biosvar.h @@ -1,4 +1,4 @@ -/* $OpenBSD: biosvar.h,v 1.50 2008/09/01 17:30:56 deraadt Exp $ */ +/* $OpenBSD: biosvar.h,v 1.51 2009/01/20 20:21:03 mlarkin Exp $ */ /* * Copyright (c) 1997-1999 Michael Shalayeff @@ -34,6 +34,9 @@ #define BOOTARG_LEN (NBPG*1) #define BOOTBIOS_ADDR (0x7c00) + /* physical page for ptp 0 need for various tramps */ +#define PTP0_PA (NBPG*3) + /* BIOS configure flags */ #define BIOSF_BIOS32 0x0001 #define BIOSF_PCIBIOS 0x0002 -- cgit v1.2.3