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

Cache object: 04114042acd0bbe52b166614498db4a0


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