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$");
   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 #ifdef SMP
   78 static cpuset_t         suspcpus;
   79 #endif
   80 
   81 static void             *acpi_alloc_wakeup_handler(void);
   82 static void             acpi_stop_beep(void *);
   83 
   84 #ifdef SMP
   85 static int              acpi_wakeup_ap(struct acpi_softc *, int);
   86 static void             acpi_wakeup_cpus(struct acpi_softc *, const cpuset_t *);
   87 #endif
   88 
   89 #define ACPI_PAGETABLES 0
   90 #define WAKECODE_VADDR(sc)      ((sc)->acpi_wakeaddr + (ACPI_PAGETABLES * PAGE_SIZE))
   91 #define WAKECODE_PADDR(sc)      ((sc)->acpi_wakephys + (ACPI_PAGETABLES * PAGE_SIZE))
   92 #define WAKECODE_FIXUP(offset, type, val) do    {       \
   93         type    *addr;                                  \
   94         addr = (type *)(WAKECODE_VADDR(sc) + offset);   \
   95         *addr = val;                                    \
   96 } while (0)
   97 
   98 static void
   99 acpi_stop_beep(void *arg)
  100 {
  101 
  102         if (acpi_resume_beep != 0)
  103                 timer_spkr_release();
  104 }
  105 
  106 #ifdef SMP
  107 static int
  108 acpi_wakeup_ap(struct acpi_softc *sc, int cpu)
  109 {
  110         int             vector = (WAKECODE_PADDR(sc) >> 12) & 0xff;
  111         int             apic_id = cpu_apic_ids[cpu];
  112         int             ms;
  113 
  114         WAKECODE_FIXUP(wakeup_pcb, struct pcb *, susppcbs[cpu]);
  115 
  116         /* do an INIT IPI: assert RESET */
  117         lapic_ipi_raw(APIC_DEST_DESTFLD | APIC_TRIGMOD_EDGE |
  118             APIC_LEVEL_ASSERT | APIC_DESTMODE_PHY | APIC_DELMODE_INIT, apic_id);
  119 
  120         /* wait for pending status end */
  121         lapic_ipi_wait(-1);
  122 
  123         /* do an INIT IPI: deassert RESET */
  124         lapic_ipi_raw(APIC_DEST_ALLESELF | APIC_TRIGMOD_LEVEL |
  125             APIC_LEVEL_DEASSERT | APIC_DESTMODE_PHY | APIC_DELMODE_INIT, 0);
  126 
  127         /* wait for pending status end */
  128         DELAY(10000);           /* wait ~10mS */
  129         lapic_ipi_wait(-1);
  130 
  131         /*
  132          * next we do a STARTUP IPI: the previous INIT IPI might still be
  133          * latched, (P5 bug) this 1st STARTUP would then terminate
  134          * immediately, and the previously started INIT IPI would continue. OR
  135          * the previous INIT IPI has already run. and this STARTUP IPI will
  136          * run. OR the previous INIT IPI was ignored. and this STARTUP IPI
  137          * will run.
  138          */
  139 
  140         /* do a STARTUP IPI */
  141         lapic_ipi_raw(APIC_DEST_DESTFLD | APIC_TRIGMOD_EDGE |
  142             APIC_LEVEL_DEASSERT | APIC_DESTMODE_PHY | APIC_DELMODE_STARTUP |
  143             vector, apic_id);
  144         lapic_ipi_wait(-1);
  145         DELAY(200);             /* wait ~200uS */
  146 
  147         /*
  148          * finally we do a 2nd STARTUP IPI: this 2nd STARTUP IPI should run IF
  149          * the previous STARTUP IPI was cancelled by a latched INIT IPI. OR
  150          * this STARTUP IPI will be ignored, as only ONE STARTUP IPI is
  151          * recognized after hardware RESET or INIT IPI.
  152          */
  153 
  154         lapic_ipi_raw(APIC_DEST_DESTFLD | APIC_TRIGMOD_EDGE |
  155             APIC_LEVEL_DEASSERT | APIC_DESTMODE_PHY | APIC_DELMODE_STARTUP |
  156             vector, apic_id);
  157         lapic_ipi_wait(-1);
  158         DELAY(200);             /* wait ~200uS */
  159 
  160         /* Wait up to 5 seconds for it to start. */
  161         for (ms = 0; ms < 5000; ms++) {
  162                 if (susppcbs[cpu]->pcb_eip == 0)
  163                         return (1);     /* return SUCCESS */
  164                 DELAY(1000);
  165         }
  166         return (0);             /* return FAILURE */
  167 }
  168 
  169 #define WARMBOOT_TARGET         0
  170 #define WARMBOOT_OFF            (KERNBASE + 0x0467)
  171 #define WARMBOOT_SEG            (KERNBASE + 0x0469)
  172 
  173 #define CMOS_REG                (0x70)
  174 #define CMOS_DATA               (0x71)
  175 #define BIOS_RESET              (0x0f)
  176 #define BIOS_WARM               (0x0a)
  177 
  178 static void
  179 acpi_wakeup_cpus(struct acpi_softc *sc, const cpuset_t *wakeup_cpus)
  180 {
  181         uint32_t        mpbioswarmvec;
  182         int             cpu;
  183         u_char          mpbiosreason;
  184 
  185         /* save the current value of the warm-start vector */
  186         mpbioswarmvec = *((uint32_t *)WARMBOOT_OFF);
  187         outb(CMOS_REG, BIOS_RESET);
  188         mpbiosreason = inb(CMOS_DATA);
  189 
  190         /* setup a vector to our boot code */
  191         *((volatile u_short *)WARMBOOT_OFF) = WARMBOOT_TARGET;
  192         *((volatile u_short *)WARMBOOT_SEG) = WAKECODE_PADDR(sc) >> 4;
  193         outb(CMOS_REG, BIOS_RESET);
  194         outb(CMOS_DATA, BIOS_WARM);     /* 'warm-start' */
  195 
  196         /* Wake up each AP. */
  197         for (cpu = 1; cpu < mp_ncpus; cpu++) {
  198                 if (!CPU_ISSET(cpu, wakeup_cpus))
  199                         continue;
  200                 if (acpi_wakeup_ap(sc, cpu) == 0) {
  201                         /* restore the warmstart vector */
  202                         *(uint32_t *)WARMBOOT_OFF = mpbioswarmvec;
  203                         panic("acpi_wakeup: failed to resume AP #%d (PHY #%d)",
  204                             cpu, cpu_apic_ids[cpu]);
  205                 }
  206         }
  207 
  208         /* restore the warmstart vector */
  209         *(uint32_t *)WARMBOOT_OFF = mpbioswarmvec;
  210 
  211         outb(CMOS_REG, BIOS_RESET);
  212         outb(CMOS_DATA, mpbiosreason);
  213 }
  214 #endif
  215 
  216 int
  217 acpi_sleep_machdep(struct acpi_softc *sc, int state)
  218 {
  219         ACPI_STATUS     status;
  220 
  221         if (sc->acpi_wakeaddr == 0ul)
  222                 return (-1);    /* couldn't alloc wake memory */
  223 
  224 #ifdef SMP
  225         suspcpus = all_cpus;
  226         CPU_CLR(PCPU_GET(cpuid), &suspcpus);
  227 #endif
  228 
  229         if (acpi_resume_beep != 0)
  230                 timer_spkr_acquire();
  231 
  232         AcpiSetFirmwareWakingVector(WAKECODE_PADDR(sc));
  233 
  234         intr_suspend();
  235 
  236         if (suspendctx(susppcbs[0])) {
  237 #ifdef SMP
  238                 if (!CPU_EMPTY(&suspcpus) &&
  239                     suspend_cpus(suspcpus) == 0) {
  240                         device_printf(sc->acpi_dev, "Failed to suspend APs\n");
  241                         return (0);     /* couldn't sleep */
  242                 }
  243 #endif
  244 
  245                 WAKECODE_FIXUP(resume_beep, uint8_t, (acpi_resume_beep != 0));
  246                 WAKECODE_FIXUP(reset_video, uint8_t, (acpi_reset_video != 0));
  247 
  248                 WAKECODE_FIXUP(wakeup_cr4, register_t, susppcbs[0]->pcb_cr4);
  249                 WAKECODE_FIXUP(wakeup_pcb, struct pcb *, susppcbs[0]);
  250 
  251                 /* Call ACPICA to enter the desired sleep state */
  252                 if (state == ACPI_STATE_S4 && sc->acpi_s4bios)
  253                         status = AcpiEnterSleepStateS4bios();
  254                 else
  255                         status = AcpiEnterSleepState(state);
  256 
  257                 if (status != AE_OK) {
  258                         device_printf(sc->acpi_dev,
  259                             "AcpiEnterSleepState failed - %s\n",
  260                             AcpiFormatException(status));
  261                         return (0);     /* couldn't sleep */
  262                 }
  263 
  264                 for (;;)
  265                         ia32_pause();
  266         }
  267 
  268         return (1);     /* wakeup successfully */
  269 }
  270 
  271 int
  272 acpi_wakeup_machdep(struct acpi_softc *sc, int state,
  273     int sleep_result, int intr_enabled)
  274 {
  275 
  276         if (sleep_result == -1)
  277                 return (sleep_result);
  278 
  279         if (intr_enabled == 0) {
  280                 /* Wakeup MD procedures in interrupt disabled context */
  281                 if (sleep_result == 1) {
  282                         pmap_init_pat();
  283                         load_cr3(susppcbs[0]->pcb_cr3);
  284                         initializecpu();
  285                         PCPU_SET(switchtime, 0);
  286                         PCPU_SET(switchticks, ticks);
  287 #ifdef SMP
  288                         if (!CPU_EMPTY(&suspcpus))
  289                                 acpi_wakeup_cpus(sc, &suspcpus);
  290 #endif
  291                 }
  292 
  293 #ifdef SMP
  294                 if (!CPU_EMPTY(&suspcpus))
  295                         restart_cpus(suspcpus);
  296 #endif
  297                 mca_resume();
  298                 intr_resume();
  299         } else {
  300                 /* Wakeup MD procedures in interrupt enabled context */
  301                 AcpiSetFirmwareWakingVector(0);
  302 
  303                 if (sleep_result == 1 && mem_range_softc.mr_op != NULL &&
  304                     mem_range_softc.mr_op->reinit != NULL)
  305                         mem_range_softc.mr_op->reinit(&mem_range_softc);
  306         }
  307 
  308         return (sleep_result);
  309 }
  310 
  311 static void *
  312 acpi_alloc_wakeup_handler(void)
  313 {
  314         void            *wakeaddr;
  315         int             i;
  316 
  317         /*
  318          * Specify the region for our wakeup code.  We want it in the low 1 MB
  319          * region, excluding real mode IVT (0-0x3ff), BDA (0x400-0x4ff), EBDA
  320          * (less than 128KB, below 0xa0000, must be excluded by SMAP and DSDT),
  321          * and ROM area (0xa0000 and above).  The temporary page tables must be
  322          * page-aligned.
  323          */
  324         wakeaddr = contigmalloc((ACPI_PAGETABLES + 1) * PAGE_SIZE, M_DEVBUF,
  325             M_NOWAIT, 0x500, 0xa0000, PAGE_SIZE, 0ul);
  326         if (wakeaddr == NULL) {
  327                 printf("%s: can't alloc wake memory\n", __func__);
  328                 return (NULL);
  329         }
  330         if (EVENTHANDLER_REGISTER(power_resume, acpi_stop_beep, NULL,
  331             EVENTHANDLER_PRI_LAST) == NULL) {
  332                 printf("%s: can't register event handler\n", __func__);
  333                 contigfree(wakeaddr, (ACPI_PAGETABLES + 1) * PAGE_SIZE, M_DEVBUF);
  334                 return (NULL);
  335         }
  336         susppcbs = malloc(mp_ncpus * sizeof(*susppcbs), M_DEVBUF, M_WAITOK);
  337         for (i = 0; i < mp_ncpus; i++) {
  338                 susppcbs[i] = malloc(sizeof(**susppcbs), M_DEVBUF, M_WAITOK);
  339         }
  340 
  341         return (wakeaddr);
  342 }
  343 
  344 void
  345 acpi_install_wakeup_handler(struct acpi_softc *sc)
  346 {
  347         static void     *wakeaddr = NULL;
  348 
  349         if (wakeaddr != NULL)
  350                 return;
  351 
  352         wakeaddr = acpi_alloc_wakeup_handler();
  353         if (wakeaddr == NULL)
  354                 return;
  355 
  356         sc->acpi_wakeaddr = (vm_offset_t)wakeaddr;
  357         sc->acpi_wakephys = vtophys(wakeaddr);
  358 
  359         bcopy(wakecode, (void *)WAKECODE_VADDR(sc), sizeof(wakecode));
  360 
  361         /* Patch GDT base address, ljmp target. */
  362         WAKECODE_FIXUP((bootgdtdesc + 2), uint32_t,
  363             WAKECODE_PADDR(sc) + bootgdt);
  364         WAKECODE_FIXUP((wakeup_sw32 + 2), uint32_t,
  365             WAKECODE_PADDR(sc) + wakeup_32);
  366 
  367         /* Save pointers to some global data. */
  368         WAKECODE_FIXUP(wakeup_ret, void *, resumectx);
  369         /*
  370          * Temporarily switch to the kernel pmap because it provides
  371          * an identity mapping (setup at boot) for the low physical
  372          * memory region containing the wakeup code.
  373          */
  374 #ifdef PAE
  375         WAKECODE_FIXUP(wakeup_cr3, register_t, vtophys(kernel_pmap->pm_pdpt));
  376 #else
  377         WAKECODE_FIXUP(wakeup_cr3, register_t, vtophys(kernel_pmap->pm_pdir));
  378 #endif
  379 
  380         if (bootverbose)
  381                 device_printf(sc->acpi_dev, "wakeup code va %#x pa %#jx\n",
  382                     sc->acpi_wakeaddr, (uintmax_t)sc->acpi_wakephys);
  383 }

Cache object: 786a4ca541892b48601c44bb791b6f45


[ 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.