diff --git a/sys/i386/acpica/acpi_machdep.c b/sys/i386/acpica/acpi_machdep.c index e0d5b6787248..867c2e68e726 100644 --- a/sys/i386/acpica/acpi_machdep.c +++ b/sys/i386/acpica/acpi_machdep.c @@ -1,362 +1,368 @@ /*- * 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. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include /* * APM driver emulation */ #include #include #include #include -uint32_t acpi_reset_video = 1; +uint32_t acpi_resume_beep; +TUNABLE_INT("hw.acpi.resume_beep", &acpi_resume_beep); +uint32_t acpi_reset_video; TUNABLE_INT("hw.acpi.reset_video", &acpi_reset_video); static int intr_model = ACPI_INTR_PIC; static int apm_active; static d_open_t apmopen; static d_close_t apmclose; static d_write_t apmwrite; static d_ioctl_t apmioctl; static d_poll_t apmpoll; static struct cdevsw apm_cdevsw = { .d_version = D_VERSION, .d_open = apmopen, .d_close = apmclose, .d_write = apmwrite, .d_ioctl = apmioctl, .d_poll = apmpoll, .d_name = "apm", }; static int acpi_capm_convert_battstate(struct acpi_battinfo *battp) { int state; state = APM_UNKNOWN; if (battp->state & ACPI_BATT_STAT_DISCHARG) { if (battp->cap >= 50) state = 0; /* high */ else state = 1; /* low */ } if (battp->state & ACPI_BATT_STAT_CRITICAL) state = 2; /* critical */ if (battp->state & ACPI_BATT_STAT_CHARGING) state = 3; /* charging */ /* If still unknown, determine it based on the battery capacity. */ if (state == APM_UNKNOWN) { if (battp->cap >= 50) state = 0; /* high */ else state = 1; /* low */ } return (state); } static int acpi_capm_convert_battflags(struct acpi_battinfo *battp) { int flags; flags = 0; if (battp->cap >= 50) flags |= APM_BATT_HIGH; else { if (battp->state & ACPI_BATT_STAT_CRITICAL) flags |= APM_BATT_CRITICAL; else flags |= APM_BATT_LOW; } if (battp->state & ACPI_BATT_STAT_CHARGING) flags |= APM_BATT_CHARGING; if (battp->state == ACPI_BATT_STAT_NOT_PRESENT) flags = APM_BATT_NOT_PRESENT; return (flags); } static int acpi_capm_get_info(apm_info_t aip) { int acline; struct acpi_battinfo batt; aip->ai_infoversion = 1; aip->ai_major = 1; aip->ai_minor = 2; aip->ai_status = apm_active; aip->ai_capabilities= 0xff00; /* unknown */ if (acpi_acad_get_acline(&acline)) aip->ai_acline = APM_UNKNOWN; /* unknown */ else aip->ai_acline = acline; /* on/off */ if (acpi_battery_get_battinfo(NULL, &batt) != 0) { aip->ai_batt_stat = APM_UNKNOWN; aip->ai_batt_life = APM_UNKNOWN; aip->ai_batt_time = -1; /* unknown */ aip->ai_batteries = ~0U; /* unknown */ } else { aip->ai_batt_stat = acpi_capm_convert_battstate(&batt); aip->ai_batt_life = batt.cap; aip->ai_batt_time = (batt.min == -1) ? -1 : batt.min * 60; aip->ai_batteries = acpi_battery_get_units(); } return (0); } static int acpi_capm_get_pwstatus(apm_pwstatus_t app) { device_t dev; int acline, unit, error; struct acpi_battinfo batt; if (app->ap_device != PMDV_ALLDEV && (app->ap_device < PMDV_BATT0 || app->ap_device > PMDV_BATT_ALL)) return (1); if (app->ap_device == PMDV_ALLDEV) error = acpi_battery_get_battinfo(NULL, &batt); else { unit = app->ap_device - PMDV_BATT0; dev = devclass_get_device(devclass_find("battery"), unit); if (dev != NULL) error = acpi_battery_get_battinfo(dev, &batt); else error = ENXIO; } if (error) return (1); app->ap_batt_stat = acpi_capm_convert_battstate(&batt); app->ap_batt_flag = acpi_capm_convert_battflags(&batt); app->ap_batt_life = batt.cap; app->ap_batt_time = (batt.min == -1) ? -1 : batt.min * 60; if (acpi_acad_get_acline(&acline)) app->ap_acline = APM_UNKNOWN; else app->ap_acline = acline; /* on/off */ return (0); } static int apmopen(struct cdev *dev, int flag, int fmt, d_thread_t *td) { return (0); } static int apmclose(struct cdev *dev, int flag, int fmt, d_thread_t *td) { return (0); } static int apmioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flag, d_thread_t *td) { int error = 0; struct acpi_softc *acpi_sc; struct apm_info info; apm_info_old_t aiop; acpi_sc = devclass_get_softc(devclass_find("acpi"), 0); switch (cmd) { case APMIO_SUSPEND: if ((flag & FWRITE) == 0) return (EPERM); if (apm_active) acpi_SetSleepState(acpi_sc, acpi_sc->acpi_suspend_sx); else error = EINVAL; break; case APMIO_STANDBY: if ((flag & FWRITE) == 0) return (EPERM); if (apm_active) acpi_SetSleepState(acpi_sc, acpi_sc->acpi_standby_sx); else error = EINVAL; break; case APMIO_GETINFO_OLD: if (acpi_capm_get_info(&info)) error = ENXIO; aiop = (apm_info_old_t)addr; aiop->ai_major = info.ai_major; aiop->ai_minor = info.ai_minor; aiop->ai_acline = info.ai_acline; aiop->ai_batt_stat = info.ai_batt_stat; aiop->ai_batt_life = info.ai_batt_life; aiop->ai_status = info.ai_status; break; case APMIO_GETINFO: if (acpi_capm_get_info((apm_info_t)addr)) error = ENXIO; break; case APMIO_GETPWSTATUS: if (acpi_capm_get_pwstatus((apm_pwstatus_t)addr)) error = ENXIO; break; case APMIO_ENABLE: if ((flag & FWRITE) == 0) return (EPERM); apm_active = 1; break; case APMIO_DISABLE: if ((flag & FWRITE) == 0) return (EPERM); apm_active = 0; break; case APMIO_HALTCPU: break; case APMIO_NOTHALTCPU: break; case APMIO_DISPLAY: if ((flag & FWRITE) == 0) return (EPERM); break; case APMIO_BIOS: if ((flag & FWRITE) == 0) return (EPERM); bzero(addr, sizeof(struct apm_bios_arg)); break; default: error = EINVAL; break; } return (error); } static int apmwrite(struct cdev *dev, struct uio *uio, int ioflag) { return (uio->uio_resid); } static int apmpoll(struct cdev *dev, int events, d_thread_t *td) { return (0); } static void acpi_capm_init(struct acpi_softc *sc) { make_dev(&apm_cdevsw, 0, 0, 5, 0664, "apm"); } int acpi_machdep_init(device_t dev) { struct acpi_softc *sc; sc = devclass_get_softc(devclass_find("acpi"), 0); acpi_capm_init(sc); acpi_install_wakeup_handler(sc); if (intr_model == ACPI_INTR_PIC) BUS_CONFIG_INTR(dev, AcpiGbl_FADT->SciInt, INTR_TRIGGER_LEVEL, INTR_POLARITY_LOW); else acpi_SetIntrModel(intr_model); + SYSCTL_ADD_UINT(&sc->acpi_sysctl_ctx, + SYSCTL_CHILDREN(sc->acpi_sysctl_tree), OID_AUTO, + "resume_beep", CTLFLAG_RD | CTLFLAG_RW, &acpi_resume_beep, 0, + "Beep the PC speaker when resuming"); SYSCTL_ADD_UINT(&sc->acpi_sysctl_ctx, SYSCTL_CHILDREN(sc->acpi_sysctl_tree), OID_AUTO, "reset_video", CTLFLAG_RD | CTLFLAG_RW, &acpi_reset_video, 0, "Call the VESA reset BIOS vector on the resume path"); return (0); } void acpi_SetDefaultIntrModel(int model) { intr_model = model; } /* Check BIOS date. If 1998 or older, disable ACPI. */ int acpi_machdep_quirks(int *quirks) { char *va; int year; /* BIOS address 0xffff5 contains the date in the format mm/dd/yy. */ va = pmap_mapdev(0xffff0, 16); sscanf(va + 11, "%2d", &year); pmap_unmapdev((vm_offset_t)va, 16); /* * Date must be >= 1/1/1999 or we don't trust ACPI. Note that this * check must be changed by my 114th birthday. */ if (year > 90 && year < 99) *quirks = ACPI_Q_BROKEN; return (0); } void acpi_cpu_c1() { __asm __volatile("sti; hlt"); } diff --git a/sys/i386/acpica/acpi_wakecode.S b/sys/i386/acpica/acpi_wakecode.S index c2d30397545d..ecbb4cdc2b32 100644 --- a/sys/i386/acpica/acpi_wakecode.S +++ b/sys/i386/acpica/acpi_wakecode.S @@ -1,239 +1,252 @@ /*- * 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. * * $FreeBSD$ */ #define LOCORE #include #include #include "assym.s" - .align 4 + .align 4096 .code16 wakeup_16: nop cli /* * Set up segment registers for real mode and a small stack for * any calls we make. */ movw %cs,%ax movw %ax,%ds movw %ax,%ss movw $PAGE_SIZE,%sp + /* 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: + /* Re-initialize video BIOS if the reset_video tunable is set. */ - cmp $0,reset_video - je wakeup_16_gdt + cmpl $1,reset_video + jne nobiosreset 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: -wakeup_16_gdt: /* Load GDT for real mode */ lgdt physical_gdt /* Restore CR2, CR3 and CR4 */ mov previous_cr2,%eax mov %eax,%cr2 mov previous_cr3,%eax mov %eax,%cr3 mov previous_cr4,%eax mov %eax,%cr4 /* Transfer some values to protected mode */ #define NVALUES 9 #define TRANSFER_STACK32(val, idx) \ mov val,%eax; \ mov %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 */ mov %cr0,%eax orl $(CR0_PE),%eax mov %eax,%cr0 wakeup_sw32: /* Switch to protected mode by intersegmental jump */ ljmpl $KCSEL,$0x12345678 /* Code location, to be replaced */ .code32 wakeup_32: /* * Switched to protected mode w/o paging * %esi: KERNEL stack pointer (physical address) */ 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 16bit code */ 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 */ lgdt PREVIOUS_GDT lidt PREVIOUS_IDT xorl %eax,%eax movl %eax,%ebx movl %eax,%ecx movl %eax,%edx movl %eax,%esi movl %eax,%edi movl PREVIOUS_DS,%ebx movl PREVIOUS_FS,%ecx movl PREVIOUS_SS,%edx movw TASK_REGISTER,%si shll $16,%esi movw PREVIOUS_LDT,%si movl RECOVER_ADDR,%edi /* Enable paging and etc. */ movl CR0_REGISTER,%eax movl %eax,%cr0 /* Flush the prefetch queue */ jmp 1f 1: jmp 1f 1: /* * Now that we are in kernel virtual memory addressing * %ebx: ds + es * %ecx: fs + gs * %edx: ss + dummy * %esi: LDTR + TR * %edi: recover address */ 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 /* Jump to acpi_restorecpu() */ jmp *%edi /* used in real mode */ physical_gdt: .word 0 .long 0 physical_esp: .long 0 previous_cr2: .long 0 previous_cr3: .long 0 previous_cr4: .long 0 +resume_beep: .long 0 reset_video: .long 0 /* transfer from real mode to protected mode */ 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 diff --git a/sys/i386/acpica/acpi_wakeup.c b/sys/i386/acpica/acpi_wakeup.c index dec1088eaf8a..543c0f2cca8f 100644 --- a/sys/i386/acpica/acpi_wakeup.c +++ b/sys/i386/acpica/acpi_wakeup.c @@ -1,336 +1,338 @@ /*- * 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. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "acpi_wakecode.h" #ifndef _SYS_CDEFS_H_ #error this file needs sys/cdefs.h as a prerequisite #endif +extern uint32_t acpi_resume_beep; extern uint32_t acpi_reset_video; extern void initializecpu(void); static struct region_descriptor saved_idt, saved_gdt, *p_gdt; static uint16_t saved_ldt; static uint32_t 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 r_cs, r_ds, r_es, r_fs, r_gs, r_ss, r_tr; static uint32_t r_esp = 0; 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; \ } while (0) #define WAKECODE_BCOPY(offset, type, val) do { \ void *addr; \ addr = (void *)(sc->acpi_wakeaddr + offset); \ bcopy(&(val), addr, sizeof(type)); \ } while (0) int acpi_sleep_machdep(struct acpi_softc *sc, int state) { ACPI_STATUS status; struct pmap *pm; vm_page_t page; int ret; uint32_t cr3; u_long ef; struct proc *p; ret = 0; if (sc->acpi_wakeaddr == 0) return (0); AcpiSetFirmwareWakingVector(sc->acpi_wakephys); ef = read_eflags(); /* Create Identity Mapping */ if ((p = curproc) == NULL) p = &proc0; pm = vmspace_pmap(p->p_vmspace); cr3 = rcr3(); #ifdef PAE load_cr3(vtophys(pm->pm_pdpt)); #else load_cr3(vtophys(pm->pm_pdir)); #endif page = PHYS_TO_VM_PAGE(sc->acpi_wakephys); pmap_enter(pm, sc->acpi_wakephys, page, VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE, 1); ret_addr = 0; ACPI_DISABLE_IRQS(); if (acpi_savecpu()) { /* Execute Sleep */ intr_suspend(); 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 (bootverbose) acpi_printcpu(); /* Call ACPICA to enter the desired sleep state */ if (state == ACPI_STATE_S4 && sc->acpi_s4bios) status = AcpiEnterSleepStateS4bios(); else status = AcpiEnterSleepState(state); if (status != AE_OK) { device_printf(sc->acpi_dev, "AcpiEnterSleepState failed - %s\n", AcpiFormatException(status)); ret = -1; goto out; } for (;;) ; } else { /* Execute Wakeup */ intr_resume(); if (bootverbose) { acpi_savecpu(); acpi_printcpu(); } } out: pmap_remove(pm, sc->acpi_wakephys, sc->acpi_wakephys + PAGE_SIZE); load_cr3(cr3); write_eflags(ef); return (ret); } static bus_dma_tag_t acpi_waketag; static bus_dmamap_t acpi_wakemap; static vm_offset_t acpi_wakeaddr = 0; static void acpi_alloc_wakeup_handler(void) { void *wakeaddr; if (!cold) return; if (bus_dma_tag_create(/* parent */ NULL, /* alignment */ 2, 0, /* lowaddr below 1MB */ 0x9ffff, /* highaddr */ BUS_SPACE_MAXADDR, NULL, NULL, PAGE_SIZE, 1, PAGE_SIZE, 0, busdma_lock_mutex, &Giant, &acpi_waketag) != 0) { printf("acpi_alloc_wakeup_handler: can't create wake tag\n"); return; } if (bus_dmamem_alloc(acpi_waketag, &wakeaddr, BUS_DMA_NOWAIT, &acpi_wakemap)) { printf("acpi_alloc_wakeup_handler: can't alloc wake memory\n"); return; } 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 = arg; uint32_t *addr; addr = (uint32_t *)&wakecode[wakeup_sw32 + 2]; *addr = segs[0].ds_addr + wakeup_32; bcopy(wakecode, (void *)sc->acpi_wakeaddr, sizeof(wakecode)); sc->acpi_wakephys = segs[0].ds_addr; } void acpi_install_wakeup_handler(struct acpi_softc *sc) { if (acpi_wakeaddr == 0) return; sc->acpi_waketag = acpi_waketag; sc->acpi_wakeaddr = acpi_wakeaddr; sc->acpi_wakemap = acpi_wakemap; bus_dmamap_load(sc->acpi_waketag, sc->acpi_wakemap, (void *)sc->acpi_wakeaddr, PAGE_SIZE, acpi_realmodeinst, sc, 0); }