From f76b5ab6ccfdff69a040a74444ecd825ccd93c4d Mon Sep 17 00:00:00 2001 From: Konstantin Belousov Date: Thu, 7 Feb 2019 02:09:34 +0000 Subject: [PATCH] Fix resume on i386 PAE. It was broken before PAE/no-PAE merge, but since now PAE is the default, resume is apparently becomes for all machines. The corrected issues: - the trampoline page is not mapped executable, so machine faults when paging is on; - MSR.EFER and %cr4 both should be loaded before paging is enabled, otherwise paging structures are invalid (cr4.PAE and EFER.NX). - MSR.EFER and %cr4 should be only loaded if present. I attempt to handle this by not touching the registers if the value is zero. There are some more bits still not quite correct, e.g. unconditional access to %cr4 in resumectx. Reported and debugging help by: bde Sponsored by: The FreeBSD Foundation MFC after: 1 week --- sys/i386/acpica/acpi_wakecode.S | 27 ++++++++++++++++++++------- sys/x86/acpica/acpi_wakeup.c | 10 ++++++++-- 2 files changed, 28 insertions(+), 9 deletions(-) diff --git a/sys/i386/acpica/acpi_wakecode.S b/sys/i386/acpica/acpi_wakecode.S index 04799d5f5a06..682dadbf359f 100644 --- a/sys/i386/acpica/acpi_wakecode.S +++ b/sys/i386/acpica/acpi_wakecode.S @@ -143,16 +143,26 @@ wakeup_32: mov $bootdata32 - bootgdt, %eax mov %ax, %ds - /* Get PCB and return address. */ - movl wakeup_pcb - wakeup_start(%ebx), %ecx - movl wakeup_ret - wakeup_start(%ebx), %edx - - /* Restore CR4 and CR3. */ - movl wakeup_cr4 - wakeup_start(%ebx), %eax + /* Restore EFER, CR4 and CR3. */ + movl wakeup_efer - wakeup_start(%ebx), %eax + movl wakeup_efer - wakeup_start + 4(%ebx), %edx + cmpl $0, %eax + jne 1f + cmpl $0, %edx + je 2f +1: movl $MSR_EFER, %ecx + wrmsr +2: movl wakeup_cr4 - wakeup_start(%ebx), %eax + cmpl $0, %eax + je 3f mov %eax, %cr4 - movl wakeup_cr3 - wakeup_start(%ebx), %eax +3: movl wakeup_cr3 - wakeup_start(%ebx), %eax mov %eax, %cr3 + /* Get PCB and return address. */ + movl wakeup_ret - wakeup_start(%ebx), %edx + movl wakeup_pcb - wakeup_start(%ebx), %ecx + /* Enable paging. */ mov %cr0, %eax orl $CR0_PG, %eax @@ -187,6 +197,9 @@ bootgdtdesc: .long bootgdt - wakeup_start /* Offset plus %ds << 4 */ ALIGN_DATA +wakeup_efer: + .long 0 + .long 0 wakeup_cr4: .long 0 wakeup_cr3: diff --git a/sys/x86/acpica/acpi_wakeup.c b/sys/x86/acpica/acpi_wakeup.c index 29012d741405..66edf2c1c715 100644 --- a/sys/x86/acpica/acpi_wakeup.c +++ b/sys/x86/acpica/acpi_wakeup.c @@ -260,6 +260,8 @@ acpi_sleep_machdep(struct acpi_softc *sc, int state) WAKECODE_FIXUP(wakeup_efer, uint64_t, rdmsr(MSR_EFER) & ~(EFER_LMA)); #else + if ((amd_feature & AMDID_NX) != 0) + WAKECODE_FIXUP(wakeup_efer, uint64_t, rdmsr(MSR_EFER)); WAKECODE_FIXUP(wakeup_cr4, register_t, pcb->pcb_cr4); #endif WAKECODE_FIXUP(wakeup_pcb, struct pcb *, pcb); @@ -375,8 +377,12 @@ acpi_alloc_wakeup_handler(void *wakepages[ACPI_WAKEPAGES]) * page-aligned. */ for (i = 0; i < ACPI_WAKEPAGES; i++) { - wakepages[i] = contigmalloc(PAGE_SIZE, M_DEVBUF, M_NOWAIT, - 0x500, 0xa0000, PAGE_SIZE, 0ul); + wakepages[i] = contigmalloc(PAGE_SIZE, M_DEVBUF, + M_NOWAIT +#ifdef __i386__ + | M_EXEC +#endif + , 0x500, 0xa0000, PAGE_SIZE, 0ul); if (wakepages[i] == NULL) { printf("%s: can't alloc wake memory\n", __func__); goto freepages;