The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/i386/acpica/acpi_wakeup.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    1 /*-
    2  * Copyright (c) 2001 Takanori Watanabe <takawata@jp.freebsd.org>
    3  * Copyright (c) 2001-2012 Mitsuru IWASAKI <iwasaki@jp.freebsd.org>
    4  * Copyright (c) 2003 Peter Wemm
    5  * Copyright (c) 2008-2012 Jung-uk Kim <jkim@FreeBSD.org>
    6  * All rights reserved.
    7  *
    8  * Redistribution and use in source and binary forms, with or without
    9  * modification, are permitted provided that the following conditions
   10  * are met:
   11  * 1. Redistributions of source code must retain the above copyright
   12  *    notice, this list of conditions and the following disclaimer.
   13  * 2. Redistributions in binary form must reproduce the above copyright
   14  *    notice, this list of conditions and the following disclaimer in the
   15  *    documentation and/or other materials provided with the distribution.
   16  *
   17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
   18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
   21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   27  * SUCH DAMAGE.
   28  */
   29 
   30 #include <sys/cdefs.h>
   31 __FBSDID("$FreeBSD: releng/9.1/sys/i386/acpica/acpi_wakeup.c 235796 2012-05-22 17:44:01Z iwasaki $");
   32 
   33 #include <sys/param.h>
   34 #include <sys/bus.h>
   35 #include <sys/eventhandler.h>
   36 #include <sys/kernel.h>
   37 #include <sys/malloc.h>
   38 #include <sys/memrange.h>
   39 #include <sys/smp.h>
   40 
   41 #include <vm/vm.h>
   42 #include <vm/pmap.h>
   43 
   44 #include <machine/clock.h>
   45 #include <machine/intr_machdep.h>
   46 #include <x86/mca.h>
   47 #include <machine/pcb.h>
   48 #include <machine/pmap.h>
   49 #include <machine/specialreg.h>
   50 #include <machine/md_var.h>
   51 
   52 #ifdef SMP
   53 #include <x86/apicreg.h>
   54 #include <machine/smp.h>
   55 #include <machine/vmparam.h>
   56 #endif
   57 
   58 #include <contrib/dev/acpica/include/acpi.h>
   59 
   60 #include <dev/acpica/acpivar.h>
   61 
   62 #include "acpi_wakecode.h"
   63 #include "acpi_wakedata.h"
   64 
   65 /* Make sure the code is less than a page and leave room for the stack. */
   66 CTASSERT(sizeof(wakecode) < PAGE_SIZE - 1024);
   67 
   68 extern int              acpi_resume_beep;
   69 extern int              acpi_reset_video;
   70 
   71 #ifdef SMP
   72 extern struct pcb       **susppcbs;
   73 #else
   74 static struct pcb       **susppcbs;
   75 #endif
   76 
   77 static void             *acpi_alloc_wakeup_handler(void);
   78 static void             acpi_stop_beep(void *);
   79 
   80 #ifdef SMP
   81 static int              acpi_wakeup_ap(struct acpi_softc *, int);
   82 static void             acpi_wakeup_cpus(struct acpi_softc *, const cpuset_t *);
   83 #endif
   84 
   85 #define ACPI_PAGETABLES 0
   86 #define WAKECODE_VADDR(sc)      ((sc)->acpi_wakeaddr + (ACPI_PAGETABLES * PAGE_SIZE))
   87 #define WAKECODE_PADDR(sc)      ((sc)->acpi_wakephys + (ACPI_PAGETABLES * PAGE_SIZE))
   88 #define WAKECODE_FIXUP(offset, type, val) do    {       \
   89         type    *addr;                                  \
   90         addr = (type *)(WAKECODE_VADDR(sc) + offset);   \
   91         *addr = val;                                    \
   92 } while (0)
   93 
   94 static void
   95 acpi_stop_beep(void *arg)
   96 {
   97 
   98         if (acpi_resume_beep != 0)
   99                 timer_spkr_release();
  100 }
  101 
  102 #ifdef SMP
  103 static int
  104 acpi_wakeup_ap(struct acpi_softc *sc, int cpu)
  105 {
  106         int             vector = (WAKECODE_PADDR(sc) >> 12) & 0xff;
  107         int             apic_id = cpu_apic_ids[cpu];
  108         int             ms;
  109 
  110         WAKECODE_FIXUP(wakeup_pcb, struct pcb *, susppcbs[cpu]);
  111 
  112         /* do an INIT IPI: assert RESET */
  113         lapic_ipi_raw(APIC_DEST_DESTFLD | APIC_TRIGMOD_EDGE |
  114             APIC_LEVEL_ASSERT | APIC_DESTMODE_PHY | APIC_DELMODE_INIT, apic_id);
  115 
  116         /* wait for pending status end */
  117         lapic_ipi_wait(-1);
  118 
  119         /* do an INIT IPI: deassert RESET */
  120         lapic_ipi_raw(APIC_DEST_ALLESELF | APIC_TRIGMOD_LEVEL |
  121             APIC_LEVEL_DEASSERT | APIC_DESTMODE_PHY | APIC_DELMODE_INIT, 0);
  122 
  123         /* wait for pending status end */
  124         DELAY(10000);           /* wait ~10mS */
  125         lapic_ipi_wait(-1);
  126 
  127         /*
  128          * next we do a STARTUP IPI: the previous INIT IPI might still be
  129          * latched, (P5 bug) this 1st STARTUP would then terminate
  130          * immediately, and the previously started INIT IPI would continue. OR
  131          * the previous INIT IPI has already run. and this STARTUP IPI will
  132          * run. OR the previous INIT IPI was ignored. and this STARTUP IPI
  133          * will run.
  134          */
  135 
  136         /* do a STARTUP IPI */
  137         lapic_ipi_raw(APIC_DEST_DESTFLD | APIC_TRIGMOD_EDGE |
  138             APIC_LEVEL_DEASSERT | APIC_DESTMODE_PHY | APIC_DELMODE_STARTUP |
  139             vector, apic_id);
  140         lapic_ipi_wait(-1);
  141         DELAY(200);             /* wait ~200uS */
  142 
  143         /*
  144          * finally we do a 2nd STARTUP IPI: this 2nd STARTUP IPI should run IF
  145          * the previous STARTUP IPI was cancelled by a latched INIT IPI. OR
  146          * this STARTUP IPI will be ignored, as only ONE STARTUP IPI is
  147          * recognized after hardware RESET or INIT IPI.
  148          */
  149 
  150         lapic_ipi_raw(APIC_DEST_DESTFLD | APIC_TRIGMOD_EDGE |
  151             APIC_LEVEL_DEASSERT | APIC_DESTMODE_PHY | APIC_DELMODE_STARTUP |
  152             vector, apic_id);
  153         lapic_ipi_wait(-1);
  154         DELAY(200);             /* wait ~200uS */
  155 
  156         /* Wait up to 5 seconds for it to start. */
  157         for (ms = 0; ms < 5000; ms++) {
  158                 if (susppcbs[cpu]->pcb_eip == 0)
  159                         return (1);     /* return SUCCESS */
  160                 DELAY(1000);
  161         }
  162         return (0);             /* return FAILURE */
  163 }
  164 
  165 #define WARMBOOT_TARGET         0
  166 #define WARMBOOT_OFF            (KERNBASE + 0x0467)
  167 #define WARMBOOT_SEG            (KERNBASE + 0x0469)
  168 
  169 #define CMOS_REG                (0x70)
  170 #define CMOS_DATA               (0x71)
  171 #define BIOS_RESET              (0x0f)
  172 #define BIOS_WARM               (0x0a)
  173 
  174 static void
  175 acpi_wakeup_cpus(struct acpi_softc *sc, const cpuset_t *wakeup_cpus)
  176 {
  177         uint32_t        mpbioswarmvec;
  178         int             cpu;
  179         u_char          mpbiosreason;
  180 
  181         /* save the current value of the warm-start vector */
  182         mpbioswarmvec = *((uint32_t *)WARMBOOT_OFF);
  183         outb(CMOS_REG, BIOS_RESET);
  184         mpbiosreason = inb(CMOS_DATA);
  185 
  186         /* setup a vector to our boot code */
  187         *((volatile u_short *)WARMBOOT_OFF) = WARMBOOT_TARGET;
  188         *((volatile u_short *)WARMBOOT_SEG) = WAKECODE_PADDR(sc) >> 4;
  189         outb(CMOS_REG, BIOS_RESET);
  190         outb(CMOS_DATA, BIOS_WARM);     /* 'warm-start' */
  191 
  192         /* Wake up each AP. */
  193         for (cpu = 1; cpu < mp_ncpus; cpu++) {
  194                 if (!CPU_ISSET(cpu, wakeup_cpus))
  195                         continue;
  196                 if (acpi_wakeup_ap(sc, cpu) == 0) {
  197                         /* restore the warmstart vector */
  198                         *(uint32_t *)WARMBOOT_OFF = mpbioswarmvec;
  199                         panic("acpi_wakeup: failed to resume AP #%d (PHY #%d)",
  200                             cpu, cpu_apic_ids[cpu]);
  201                 }
  202         }
  203 
  204         /* restore the warmstart vector */
  205         *(uint32_t *)WARMBOOT_OFF = mpbioswarmvec;
  206 
  207         outb(CMOS_REG, BIOS_RESET);
  208         outb(CMOS_DATA, mpbiosreason);
  209 }
  210 #endif
  211 
  212 int
  213 acpi_sleep_machdep(struct acpi_softc *sc, int state)
  214 {
  215 #ifdef SMP
  216         cpuset_t        wakeup_cpus;
  217 #endif
  218         register_t      cr3, rf;
  219         ACPI_STATUS     status;
  220         struct pmap     *pm;
  221         int             ret;
  222 
  223         ret = -1;
  224 
  225         if (sc->acpi_wakeaddr == 0ul)
  226                 return (ret);
  227 
  228 #ifdef SMP
  229         wakeup_cpus = all_cpus;
  230         CPU_CLR(PCPU_GET(cpuid), &wakeup_cpus);
  231 #endif
  232 
  233         if (acpi_resume_beep != 0)
  234                 timer_spkr_acquire();
  235 
  236         AcpiSetFirmwareWakingVector(WAKECODE_PADDR(sc));
  237 
  238         rf = intr_disable();
  239         intr_suspend();
  240 
  241         /*
  242          * Temporarily switch to the kernel pmap because it provides
  243          * an identity mapping (setup at boot) for the low physical
  244          * memory region containing the wakeup code.
  245          */
  246         pm = kernel_pmap;
  247         cr3 = rcr3();
  248 #ifdef PAE
  249         load_cr3(vtophys(pm->pm_pdpt));
  250 #else
  251         load_cr3(vtophys(pm->pm_pdir));
  252 #endif
  253 
  254         if (suspendctx(susppcbs[0])) {
  255 #ifdef SMP
  256                 if (!CPU_EMPTY(&wakeup_cpus) &&
  257                     suspend_cpus(wakeup_cpus) == 0) {
  258                         device_printf(sc->acpi_dev, "Failed to suspend APs\n");
  259                         goto out;
  260                 }
  261 #endif
  262 
  263                 WAKECODE_FIXUP(resume_beep, uint8_t, (acpi_resume_beep != 0));
  264                 WAKECODE_FIXUP(reset_video, uint8_t, (acpi_reset_video != 0));
  265 
  266                 WAKECODE_FIXUP(wakeup_cr4, register_t, susppcbs[0]->pcb_cr4);
  267                 WAKECODE_FIXUP(wakeup_cr3, register_t, susppcbs[0]->pcb_cr3);
  268 
  269                 WAKECODE_FIXUP(wakeup_pcb, struct pcb *, susppcbs[0]);
  270 
  271                 /* Call ACPICA to enter the desired sleep state */
  272                 if (state == ACPI_STATE_S4 && sc->acpi_s4bios)
  273                         status = AcpiEnterSleepStateS4bios();
  274                 else
  275                         status = AcpiEnterSleepState(state);
  276 
  277                 if (status != AE_OK) {
  278                         device_printf(sc->acpi_dev,
  279                             "AcpiEnterSleepState failed - %s\n",
  280                             AcpiFormatException(status));
  281                         goto out;
  282                 }
  283 
  284                 for (;;)
  285                         ia32_pause();
  286         } else {
  287                 pmap_init_pat();
  288                 initializecpu();
  289                 PCPU_SET(switchtime, 0);
  290                 PCPU_SET(switchticks, ticks);
  291 #ifdef SMP
  292                 if (!CPU_EMPTY(&wakeup_cpus))
  293                         acpi_wakeup_cpus(sc, &wakeup_cpus);
  294 #endif
  295                 ret = 0;
  296         }
  297 
  298 out:
  299 #ifdef SMP
  300         if (!CPU_EMPTY(&wakeup_cpus))
  301                 restart_cpus(wakeup_cpus);
  302 #endif
  303 
  304         load_cr3(cr3);
  305         mca_resume();
  306         intr_resume();
  307         intr_restore(rf);
  308 
  309         AcpiSetFirmwareWakingVector(0);
  310 
  311         if (ret == 0 && mem_range_softc.mr_op != NULL &&
  312             mem_range_softc.mr_op->reinit != NULL)
  313                 mem_range_softc.mr_op->reinit(&mem_range_softc);
  314 
  315         return (ret);
  316 }
  317 
  318 static void *
  319 acpi_alloc_wakeup_handler(void)
  320 {
  321         void            *wakeaddr;
  322         int             i;
  323 
  324         /*
  325          * Specify the region for our wakeup code.  We want it in the low 1 MB
  326          * region, excluding real mode IVT (0-0x3ff), BDA (0x400-0x4ff), EBDA
  327          * (less than 128KB, below 0xa0000, must be excluded by SMAP and DSDT),
  328          * and ROM area (0xa0000 and above).  The temporary page tables must be
  329          * page-aligned.
  330          */
  331         wakeaddr = contigmalloc((ACPI_PAGETABLES + 1) * PAGE_SIZE, M_DEVBUF,
  332             M_NOWAIT, 0x500, 0xa0000, PAGE_SIZE, 0ul);
  333         if (wakeaddr == NULL) {
  334                 printf("%s: can't alloc wake memory\n", __func__);
  335                 return (NULL);
  336         }
  337         if (EVENTHANDLER_REGISTER(power_resume, acpi_stop_beep, NULL,
  338             EVENTHANDLER_PRI_LAST) == NULL) {
  339                 printf("%s: can't register event handler\n", __func__);
  340                 contigfree(wakeaddr, (ACPI_PAGETABLES + 1) * PAGE_SIZE, M_DEVBUF);
  341                 return (NULL);
  342         }
  343         susppcbs = malloc(mp_ncpus * sizeof(*susppcbs), M_DEVBUF, M_WAITOK);
  344         for (i = 0; i < mp_ncpus; i++) {
  345                 susppcbs[i] = malloc(sizeof(**susppcbs), M_DEVBUF, M_WAITOK);
  346         }
  347 
  348         return (wakeaddr);
  349 }
  350 
  351 void
  352 acpi_install_wakeup_handler(struct acpi_softc *sc)
  353 {
  354         static void     *wakeaddr = NULL;
  355 
  356         if (wakeaddr != NULL)
  357                 return;
  358 
  359         wakeaddr = acpi_alloc_wakeup_handler();
  360         if (wakeaddr == NULL)
  361                 return;
  362 
  363         sc->acpi_wakeaddr = (vm_offset_t)wakeaddr;
  364         sc->acpi_wakephys = vtophys(wakeaddr);
  365 
  366         bcopy(wakecode, (void *)WAKECODE_VADDR(sc), sizeof(wakecode));
  367 
  368         /* Patch GDT base address, ljmp target. */
  369         WAKECODE_FIXUP((bootgdtdesc + 2), uint32_t,
  370             WAKECODE_PADDR(sc) + bootgdt);
  371         WAKECODE_FIXUP((wakeup_sw32 + 2), uint32_t,
  372             WAKECODE_PADDR(sc) + wakeup_32);
  373 
  374         /* Save pointers to some global data. */
  375         WAKECODE_FIXUP(wakeup_ret, void *, resumectx);
  376 
  377         if (bootverbose)
  378                 device_printf(sc->acpi_dev, "wakeup code va %#x pa %#jx\n",
  379                     sc->acpi_wakeaddr, (uintmax_t)sc->acpi_wakephys);
  380 }

Cache object: 89c6f04f4b994ad0fb4f6b831fd2b261


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.