--- i386/acpica/acpi_wakeup.c 2012-06-02 21:42:36.000000000 +0900 +++ x86/acpica/acpi_wakeup.c 2012-06-02 21:57:38.000000000 +0900 @@ -28,7 +28,7 @@ */ #include -__FBSDID("$FreeBSD: src/sys/i386/acpica/acpi_wakeup.c,v 1.62 2012/06/01 21:33:33 jkim Exp $"); +__FBSDID("$FreeBSD: src/sys/amd64/acpica/acpi_wakeup.c,v 1.58 2012/06/01 21:33:33 jkim Exp $"); #include #include @@ -83,7 +83,12 @@ static void acpi_wakeup_cpus(struct acpi_softc *); #endif +#ifdef __amd64__ +#define ACPI_PAGETABLES 3 +#else #define ACPI_PAGETABLES 0 +#endif + #define WAKECODE_VADDR(sc) ((sc)->acpi_wakeaddr + (ACPI_PAGETABLES * PAGE_SIZE)) #define WAKECODE_PADDR(sc) ((sc)->acpi_wakephys + (ACPI_PAGETABLES * PAGE_SIZE)) #define WAKECODE_FIXUP(offset, type, val) do { \ @@ -109,6 +114,10 @@ int ms; WAKECODE_FIXUP(wakeup_pcb, struct pcb *, susppcbs[cpu]); + WAKECODE_FIXUP(wakeup_gdt, uint16_t, susppcbs[cpu]->pcb_gdt.rd_limit); + WAKECODE_FIXUP(wakeup_gdt + 2, uint64_t, + susppcbs[cpu]->pcb_gdt.rd_base); + susppcbs[cpu]->pcb_flags |= PCB_SUSPENDING; /* do an INIT IPI: assert RESET */ lapic_ipi_raw(APIC_DEST_DESTFLD | APIC_TRIGMOD_EDGE | @@ -156,7 +165,7 @@ /* Wait up to 5 seconds for it to start. */ for (ms = 0; ms < 5000; ms++) { - if (susppcbs[cpu]->pcb_eip == 0) + if ((susppcbs[cpu]->pcb_flags & PCB_SUSPENDING) == 0) return (1); /* return SUCCESS */ DELAY(1000); } @@ -231,6 +240,9 @@ intr_suspend(); if (suspendctx(susppcbs[0])) { +#ifdef __amd64__ + ctx_fpusave(susppcbs[0]->pcb_fpususpend); +#endif #ifdef SMP if (!CPU_EMPTY(&suspcpus) && suspend_cpus(suspcpus) == 0) { device_printf(sc->acpi_dev, "Failed to suspend APs\n"); @@ -243,6 +255,10 @@ WAKECODE_FIXUP(wakeup_cr4, register_t, susppcbs[0]->pcb_cr4); WAKECODE_FIXUP(wakeup_pcb, struct pcb *, susppcbs[0]); + WAKECODE_FIXUP(wakeup_gdt, uint16_t, + susppcbs[0]->pcb_gdt.rd_limit); + WAKECODE_FIXUP(wakeup_gdt + 2, uint64_t, + susppcbs[0]->pcb_gdt.rd_base); /* Call ACPICA to enter the desired sleep state */ if (state == ACPI_STATE_S4 && sc->acpi_s4bios) @@ -317,7 +333,7 @@ * page-aligned. */ wakeaddr = contigmalloc((ACPI_PAGETABLES + 1) * PAGE_SIZE, M_DEVBUF, - M_NOWAIT, 0x500, 0xa0000, PAGE_SIZE, 0ul); + M_WAITOK, 0x500, 0xa0000, PAGE_SIZE, 0ul); if (wakeaddr == NULL) { printf("%s: can't alloc wake memory\n", __func__); return (NULL); @@ -331,6 +347,9 @@ susppcbs = malloc(mp_ncpus * sizeof(*susppcbs), M_DEVBUF, M_WAITOK); for (i = 0; i < mp_ncpus; i++) { susppcbs[i] = malloc(sizeof(**susppcbs), M_DEVBUF, M_WAITOK); +#ifdef __amd64__ + susppcbs[i]->pcb_fpususpend = alloc_fpusave(M_WAITOK); +#endif } return (wakeaddr); @@ -340,6 +359,10 @@ acpi_install_wakeup_handler(struct acpi_softc *sc) { static void *wakeaddr = NULL; +#ifdef __amd64__ + uint64_t *pt4, *pt3, *pt2; + int i; +#endif if (wakeaddr != NULL) return; @@ -353,26 +376,63 @@ bcopy(wakecode, (void *)WAKECODE_VADDR(sc), sizeof(wakecode)); - /* Patch GDT base address, ljmp target. */ + /* Patch GDT base address, ljmp targets. */ WAKECODE_FIXUP((bootgdtdesc + 2), uint32_t, WAKECODE_PADDR(sc) + bootgdt); WAKECODE_FIXUP((wakeup_sw32 + 2), uint32_t, WAKECODE_PADDR(sc) + wakeup_32); +#ifdef __amd64__ + WAKECODE_FIXUP((wakeup_sw64 + 1), uint32_t, + WAKECODE_PADDR(sc) + wakeup_64); + WAKECODE_FIXUP(wakeup_pagetables, uint32_t, sc->acpi_wakephys); +#endif /* Save pointers to some global data. */ WAKECODE_FIXUP(wakeup_ret, void *, resumectx); - /* - * Temporarily switch to the kernel pmap because it provides - * an identity mapping (setup at boot) for the low physical - * memory region containing the wakeup code. - */ +#ifdef __amd64__ + WAKECODE_FIXUP(wakeup_cr3, uint64_t, KPML4phys); +#else #ifdef PAE WAKECODE_FIXUP(wakeup_cr3, register_t, vtophys(kernel_pmap->pm_pdpt)); #else WAKECODE_FIXUP(wakeup_cr3, register_t, vtophys(kernel_pmap->pm_pdir)); #endif +#endif + +#ifdef __amd64__ + /* Build temporary page tables below realmode code. */ + pt4 = wakeaddr; + pt3 = pt4 + (PAGE_SIZE) / sizeof(uint64_t); + pt2 = pt3 + (PAGE_SIZE) / sizeof(uint64_t); + + /* Create the initial 1GB replicated page tables */ + for (i = 0; i < 512; i++) { + /* + * Each slot of the level 4 pages points + * to the same level 3 page + */ + pt4[i] = (uint64_t)(sc->acpi_wakephys + PAGE_SIZE); + pt4[i] |= PG_V | PG_RW | PG_U; + + /* + * Each slot of the level 3 pages points + * to the same level 2 page + */ + pt3[i] = (uint64_t)(sc->acpi_wakephys + (2 * PAGE_SIZE)); + pt3[i] |= PG_V | PG_RW | PG_U; + + /* The level 2 page slots are mapped with 2MB pages for 1GB. */ + pt2[i] = i * (2 * 1024 * 1024); + pt2[i] |= PG_V | PG_RW | PG_PS | PG_U; + } +#endif if (bootverbose) +#ifdef __amd64__ + device_printf(sc->acpi_dev, "wakeup code va %p pa %p\n", + (void *)sc->acpi_wakeaddr, (void *)sc->acpi_wakephys); +#else device_printf(sc->acpi_dev, "wakeup code va %#x pa %#jx\n", sc->acpi_wakeaddr, (uintmax_t)sc->acpi_wakephys); +#endif }