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
This commit is contained in:
Konstantin Belousov 2019-02-07 02:09:34 +00:00
parent d22ff6e6a2
commit f76b5ab6cc
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=343851
2 changed files with 28 additions and 9 deletions

View File

@ -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:

View File

@ -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;