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/amd64/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 Mitsuru IWASAKI <iwasaki@jp.freebsd.org>
    4  * Copyright (c) 2003 Peter Wemm
    5  * Copyright (c) 2008-2010 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/8.4/sys/amd64/acpica/acpi_wakeup.c 237009 2012-06-13 15:25:52Z jhb $");
   32 
   33 #include <sys/param.h>
   34 #include <sys/bus.h>
   35 #include <sys/kernel.h>
   36 #include <sys/malloc.h>
   37 #include <sys/memrange.h>
   38 #include <sys/smp.h>
   39 
   40 #include <vm/vm.h>
   41 #include <vm/pmap.h>
   42 
   43 #include <machine/intr_machdep.h>
   44 #include <machine/mca.h>
   45 #include <machine/pcb.h>
   46 #include <machine/pmap.h>
   47 #include <machine/specialreg.h>
   48 #include <machine/md_var.h>
   49 
   50 #ifdef SMP
   51 #include <machine/apicreg.h>
   52 #include <machine/smp.h>
   53 #include <machine/vmparam.h>
   54 #endif
   55 
   56 #include <contrib/dev/acpica/include/acpi.h>
   57 
   58 #include <dev/acpica/acpivar.h>
   59 
   60 #include "acpi_wakecode.h"
   61 #include "acpi_wakedata.h"
   62 
   63 /* Make sure the code is less than a page and leave room for the stack. */
   64 CTASSERT(sizeof(wakecode) < PAGE_SIZE - 1024);
   65 
   66 extern int              acpi_resume_beep;
   67 extern int              acpi_reset_video;
   68 
   69 #ifdef SMP
   70 extern struct pcb       **susppcbs;
   71 extern void             **suspfpusave;
   72 #else
   73 static struct pcb       **susppcbs;
   74 static void             **suspfpusave;
   75 #endif
   76 
   77 int                     acpi_restorecpu(vm_offset_t, struct pcb *);
   78 
   79 static void             *acpi_alloc_wakeup_handler(void);
   80 static void             acpi_stop_beep(void *);
   81 
   82 #ifdef SMP
   83 static int              acpi_wakeup_ap(struct acpi_softc *, int);
   84 static void             acpi_wakeup_cpus(struct acpi_softc *, cpumask_t);
   85 #endif
   86 
   87 #define WAKECODE_VADDR(sc)      ((sc)->acpi_wakeaddr + (3 * PAGE_SIZE))
   88 #define WAKECODE_PADDR(sc)      ((sc)->acpi_wakephys + (3 * PAGE_SIZE))
   89 #define WAKECODE_FIXUP(offset, type, val) do    {       \
   90         type    *addr;                                  \
   91         addr = (type *)(WAKECODE_VADDR(sc) + offset);   \
   92         *addr = val;                                    \
   93 } while (0)
   94 
   95 /* Turn off bits 1&2 of the PIT, stopping the beep. */
   96 static void
   97 acpi_stop_beep(void *arg)
   98 {
   99         outb(0x61, inb(0x61) & ~0x3);
  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         WAKECODE_FIXUP(wakeup_fpusave, void *, suspfpusave[cpu]);
  112         WAKECODE_FIXUP(wakeup_gdt, uint16_t, susppcbs[cpu]->pcb_gdt.rd_limit);
  113         WAKECODE_FIXUP(wakeup_gdt + 2, uint64_t,
  114             susppcbs[cpu]->pcb_gdt.rd_base);
  115         WAKECODE_FIXUP(wakeup_cpu, int, cpu);
  116 
  117         /* do an INIT IPI: assert RESET */
  118         lapic_ipi_raw(APIC_DEST_DESTFLD | APIC_TRIGMOD_EDGE |
  119             APIC_LEVEL_ASSERT | APIC_DESTMODE_PHY | APIC_DELMODE_INIT, apic_id);
  120 
  121         /* wait for pending status end */
  122         lapic_ipi_wait(-1);
  123 
  124         /* do an INIT IPI: deassert RESET */
  125         lapic_ipi_raw(APIC_DEST_ALLESELF | APIC_TRIGMOD_LEVEL |
  126             APIC_LEVEL_DEASSERT | APIC_DESTMODE_PHY | APIC_DELMODE_INIT, 0);
  127 
  128         /* wait for pending status end */
  129         DELAY(10000);           /* wait ~10mS */
  130         lapic_ipi_wait(-1);
  131 
  132         /*
  133          * next we do a STARTUP IPI: the previous INIT IPI might still be
  134          * latched, (P5 bug) this 1st STARTUP would then terminate
  135          * immediately, and the previously started INIT IPI would continue. OR
  136          * the previous INIT IPI has already run. and this STARTUP IPI will
  137          * run. OR the previous INIT IPI was ignored. and this STARTUP IPI
  138          * will run.
  139          */
  140 
  141         /* do a STARTUP IPI */
  142         lapic_ipi_raw(APIC_DEST_DESTFLD | APIC_TRIGMOD_EDGE |
  143             APIC_LEVEL_DEASSERT | APIC_DESTMODE_PHY | APIC_DELMODE_STARTUP |
  144             vector, apic_id);
  145         lapic_ipi_wait(-1);
  146         DELAY(200);             /* wait ~200uS */
  147 
  148         /*
  149          * finally we do a 2nd STARTUP IPI: this 2nd STARTUP IPI should run IF
  150          * the previous STARTUP IPI was cancelled by a latched INIT IPI. OR
  151          * this STARTUP IPI will be ignored, as only ONE STARTUP IPI is
  152          * recognized after hardware RESET or INIT IPI.
  153          */
  154 
  155         lapic_ipi_raw(APIC_DEST_DESTFLD | APIC_TRIGMOD_EDGE |
  156             APIC_LEVEL_DEASSERT | APIC_DESTMODE_PHY | APIC_DELMODE_STARTUP |
  157             vector, apic_id);
  158         lapic_ipi_wait(-1);
  159         DELAY(200);             /* wait ~200uS */
  160 
  161         /* Wait up to 5 seconds for it to start. */
  162         for (ms = 0; ms < 5000; ms++) {
  163                 if (*(int *)(WAKECODE_VADDR(sc) + wakeup_cpu) == 0)
  164                         return (1);     /* return SUCCESS */
  165                 DELAY(1000);
  166         }
  167         return (0);             /* return FAILURE */
  168 }
  169 
  170 #define WARMBOOT_TARGET         0
  171 #define WARMBOOT_OFF            (KERNBASE + 0x0467)
  172 #define WARMBOOT_SEG            (KERNBASE + 0x0469)
  173 
  174 #define CMOS_REG                (0x70)
  175 #define CMOS_DATA               (0x71)
  176 #define BIOS_RESET              (0x0f)
  177 #define BIOS_WARM               (0x0a)
  178 
  179 static void
  180 acpi_wakeup_cpus(struct acpi_softc *sc, cpumask_t wakeup_cpus)
  181 {
  182         uint32_t        mpbioswarmvec;
  183         int             cpu;
  184         u_char          mpbiosreason;
  185 
  186         /* save the current value of the warm-start vector */
  187         mpbioswarmvec = *((uint32_t *)WARMBOOT_OFF);
  188         outb(CMOS_REG, BIOS_RESET);
  189         mpbiosreason = inb(CMOS_DATA);
  190 
  191         /* setup a vector to our boot code */
  192         *((volatile u_short *)WARMBOOT_OFF) = WARMBOOT_TARGET;
  193         *((volatile u_short *)WARMBOOT_SEG) = WAKECODE_PADDR(sc) >> 4;
  194         outb(CMOS_REG, BIOS_RESET);
  195         outb(CMOS_DATA, BIOS_WARM);     /* 'warm-start' */
  196 
  197         /* Wake up each AP. */
  198         for (cpu = 1; cpu < mp_ncpus; cpu++) {
  199                 if ((wakeup_cpus & (1 << cpu)) == 0)
  200                         continue;
  201                 if (acpi_wakeup_ap(sc, cpu) == 0) {
  202                         /* restore the warmstart vector */
  203                         *(uint32_t *)WARMBOOT_OFF = mpbioswarmvec;
  204                         panic("acpi_wakeup: failed to resume AP #%d (PHY #%d)",
  205                             cpu, cpu_apic_ids[cpu]);
  206                 }
  207         }
  208 
  209         /* restore the warmstart vector */
  210         *(uint32_t *)WARMBOOT_OFF = mpbioswarmvec;
  211 
  212         outb(CMOS_REG, BIOS_RESET);
  213         outb(CMOS_DATA, mpbiosreason);
  214 }
  215 #endif
  216 
  217 int
  218 acpi_sleep_machdep(struct acpi_softc *sc, int state)
  219 {
  220 #ifdef SMP
  221         cpumask_t       wakeup_cpus;
  222 #endif
  223         register_t      cr3, rf;
  224         ACPI_STATUS     status;
  225         int             ret;
  226 
  227         ret = -1;
  228 
  229         if (sc->acpi_wakeaddr == 0ul)
  230                 return (ret);
  231 
  232 #ifdef SMP
  233         wakeup_cpus = PCPU_GET(other_cpus);
  234 #endif
  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         cr3 = rcr3();
  247         load_cr3(KPML4phys);
  248 
  249         if (savectx(susppcbs[0])) {
  250                 ctx_fpusave(suspfpusave[0]);
  251 #ifdef SMP
  252                 if (wakeup_cpus != 0 && suspend_cpus(wakeup_cpus) == 0) {
  253                         device_printf(sc->acpi_dev,
  254                             "Failed to suspend APs: CPU mask = 0x%jx\n",
  255                             (uintmax_t)(wakeup_cpus & ~stopped_cpus));
  256                         goto out;
  257                 }
  258 #endif
  259 
  260                 WAKECODE_FIXUP(resume_beep, uint8_t, (acpi_resume_beep != 0));
  261                 WAKECODE_FIXUP(reset_video, uint8_t, (acpi_reset_video != 0));
  262 
  263                 WAKECODE_FIXUP(wakeup_pcb, struct pcb *, susppcbs[0]);
  264                 WAKECODE_FIXUP(wakeup_fpusave, void *, suspfpusave[0]);
  265                 WAKECODE_FIXUP(wakeup_gdt, uint16_t,
  266                     susppcbs[0]->pcb_gdt.rd_limit);
  267                 WAKECODE_FIXUP(wakeup_gdt + 2, uint64_t,
  268                     susppcbs[0]->pcb_gdt.rd_base);
  269                 WAKECODE_FIXUP(wakeup_cpu, int, 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                 PCPU_SET(switchtime, 0);
  289                 PCPU_SET(switchticks, ticks);
  290 #ifdef SMP
  291                 if (wakeup_cpus != 0)
  292                         acpi_wakeup_cpus(sc, wakeup_cpus);
  293 #endif
  294                 acpi_resync_clock(sc);
  295                 ret = 0;
  296         }
  297 
  298 out:
  299 #ifdef SMP
  300         if (wakeup_cpus != 0)
  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         /* If we beeped, turn it off after a delay. */
  316         if (acpi_resume_beep)
  317                 timeout(acpi_stop_beep, NULL, 3 * hz);
  318 
  319         return (ret);
  320 }
  321 
  322 static void *
  323 acpi_alloc_wakeup_handler(void)
  324 {
  325         void            *wakeaddr;
  326         int             i;
  327 
  328         /*
  329          * Specify the region for our wakeup code.  We want it in the low 1 MB
  330          * region, excluding real mode IVT (0-0x3ff), BDA (0x400-0x4ff), EBDA
  331          * (less than 128KB, below 0xa0000, must be excluded by SMAP and DSDT),
  332          * and ROM area (0xa0000 and above).  The temporary page tables must be
  333          * page-aligned.
  334          */
  335         wakeaddr = contigmalloc(4 * PAGE_SIZE, M_DEVBUF, M_NOWAIT, 0x500,
  336             0xa0000, PAGE_SIZE, 0ul);
  337         if (wakeaddr == NULL) {
  338                 printf("%s: can't alloc wake memory\n", __func__);
  339                 return (NULL);
  340         }
  341         susppcbs = malloc(mp_ncpus * sizeof(*susppcbs), M_DEVBUF, M_WAITOK);
  342         suspfpusave = malloc(mp_ncpus * sizeof(void *), M_DEVBUF, M_WAITOK);
  343         for (i = 0; i < mp_ncpus; i++) {
  344                 susppcbs[i] = malloc(sizeof(**susppcbs), M_DEVBUF, M_WAITOK);
  345                 suspfpusave[i] = alloc_fpusave(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         uint64_t        *pt4, *pt3, *pt2;
  356         int             i;
  357 
  358         if (wakeaddr != NULL)
  359                 return;
  360 
  361         wakeaddr = acpi_alloc_wakeup_handler();
  362         if (wakeaddr == NULL)
  363                 return;
  364 
  365         sc->acpi_wakeaddr = (vm_offset_t)wakeaddr;
  366         sc->acpi_wakephys = vtophys(wakeaddr);
  367 
  368         bcopy(wakecode, (void *)WAKECODE_VADDR(sc), sizeof(wakecode));
  369 
  370         /* Patch GDT base address, ljmp targets and page table base address. */
  371         WAKECODE_FIXUP((bootgdtdesc + 2), uint32_t,
  372             WAKECODE_PADDR(sc) + bootgdt);
  373         WAKECODE_FIXUP((wakeup_sw32 + 2), uint32_t,
  374             WAKECODE_PADDR(sc) + wakeup_32);
  375         WAKECODE_FIXUP((wakeup_sw64 + 1), uint32_t,
  376             WAKECODE_PADDR(sc) + wakeup_64);
  377         WAKECODE_FIXUP(wakeup_pagetables, uint32_t, sc->acpi_wakephys);
  378 
  379         /* Save pointers to some global data. */
  380         WAKECODE_FIXUP(wakeup_retaddr, void *, acpi_restorecpu);
  381         WAKECODE_FIXUP(wakeup_kpml4, uint64_t, KPML4phys);
  382         WAKECODE_FIXUP(wakeup_ctx, vm_offset_t,
  383             WAKECODE_VADDR(sc) + wakeup_ctx);
  384         WAKECODE_FIXUP(wakeup_efer, uint64_t, rdmsr(MSR_EFER));
  385         WAKECODE_FIXUP(wakeup_star, uint64_t, rdmsr(MSR_STAR));
  386         WAKECODE_FIXUP(wakeup_lstar, uint64_t, rdmsr(MSR_LSTAR));
  387         WAKECODE_FIXUP(wakeup_cstar, uint64_t, rdmsr(MSR_CSTAR));
  388         WAKECODE_FIXUP(wakeup_sfmask, uint64_t, rdmsr(MSR_SF_MASK));
  389 
  390         /* Build temporary page tables below realmode code. */
  391         pt4 = wakeaddr;
  392         pt3 = pt4 + (PAGE_SIZE) / sizeof(uint64_t);
  393         pt2 = pt3 + (PAGE_SIZE) / sizeof(uint64_t);
  394 
  395         /* Create the initial 1GB replicated page tables */
  396         for (i = 0; i < 512; i++) {
  397                 /*
  398                  * Each slot of the level 4 pages points
  399                  * to the same level 3 page
  400                  */
  401                 pt4[i] = (uint64_t)(sc->acpi_wakephys + PAGE_SIZE);
  402                 pt4[i] |= PG_V | PG_RW | PG_U;
  403 
  404                 /*
  405                  * Each slot of the level 3 pages points
  406                  * to the same level 2 page
  407                  */
  408                 pt3[i] = (uint64_t)(sc->acpi_wakephys + (2 * PAGE_SIZE));
  409                 pt3[i] |= PG_V | PG_RW | PG_U;
  410 
  411                 /* The level 2 page slots are mapped with 2MB pages for 1GB. */
  412                 pt2[i] = i * (2 * 1024 * 1024);
  413                 pt2[i] |= PG_V | PG_RW | PG_PS | PG_U;
  414         }
  415 
  416         if (bootverbose)
  417                 device_printf(sc->acpi_dev, "wakeup code va %p pa %p\n",
  418                     (void *)sc->acpi_wakeaddr, (void *)sc->acpi_wakephys);
  419 }

Cache object: d13856aaaf6edf537d2dbf44e615d97d


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