4b4f7280d7
Some Dell laptops enter resume with apparent garbage in the segment descriptor registers (almost certainly the result of a botched transition from protected to real mode.) The only way to clean that up is to enter protected mode ourselves and clean out the descriptor registers. This fixes resume on Dell XPS M1210 and Dell D620. Reference: http://bugzilla.kernel.org/show_bug.cgi?id=10927 Signed-off-by: H. Peter Anvin <hpa@zytor.com> Cc: Andrew Morton <akpm@linux-foundation.org> Cc: Pavel Machek <pavel@ucw.cz> Cc: pm list <linux-pm@lists.linux-foundation.org> Cc: Len Brown <lenb@kernel.org> Signed-off-by: Ingo Molnar <mingo@elte.hu> Tested-by: Kirill A. Shutemov <kirill@shutemov.name> Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl> Signed-off-by: Ingo Molnar <mingo@elte.hu>
150 lines
2.6 KiB
ArmAsm
150 lines
2.6 KiB
ArmAsm
/*
|
|
* ACPI wakeup real mode startup stub
|
|
*/
|
|
#include <asm/segment.h>
|
|
#include <asm/msr-index.h>
|
|
#include <asm/page.h>
|
|
#include <asm/pgtable.h>
|
|
#include <asm/processor-flags.h>
|
|
|
|
.code16
|
|
.section ".header", "a"
|
|
|
|
/* This should match the structure in wakeup.h */
|
|
.globl wakeup_header
|
|
wakeup_header:
|
|
video_mode: .short 0 /* Video mode number */
|
|
pmode_return: .byte 0x66, 0xea /* ljmpl */
|
|
.long 0 /* offset goes here */
|
|
.short __KERNEL_CS
|
|
pmode_cr0: .long 0 /* Saved %cr0 */
|
|
pmode_cr3: .long 0 /* Saved %cr3 */
|
|
pmode_cr4: .long 0 /* Saved %cr4 */
|
|
pmode_efer: .quad 0 /* Saved EFER */
|
|
pmode_gdt: .quad 0
|
|
realmode_flags: .long 0
|
|
real_magic: .long 0
|
|
trampoline_segment: .word 0
|
|
_pad1: .byte 0
|
|
wakeup_jmp: .byte 0xea /* ljmpw */
|
|
wakeup_jmp_off: .word 3f
|
|
wakeup_jmp_seg: .word 0
|
|
wakeup_gdt: .quad 0, 0, 0
|
|
signature: .long 0x51ee1111
|
|
|
|
.text
|
|
.globl _start
|
|
.code16
|
|
wakeup_code:
|
|
_start:
|
|
cli
|
|
cld
|
|
|
|
/* Apparently some dimwit BIOS programmers don't know how to
|
|
program a PM to RM transition, and we might end up here with
|
|
junk in the data segment descriptor registers. The only way
|
|
to repair that is to go into PM and fix it ourselves... */
|
|
movw $16, %cx
|
|
lgdtl %cs:wakeup_gdt
|
|
movl %cr0, %eax
|
|
orb $X86_CR0_PE, %al
|
|
movl %eax, %cr0
|
|
jmp 1f
|
|
1: ljmpw $8, $2f
|
|
2:
|
|
movw %cx, %ds
|
|
movw %cx, %es
|
|
movw %cx, %ss
|
|
movw %cx, %fs
|
|
movw %cx, %gs
|
|
|
|
andb $~X86_CR0_PE, %al
|
|
movl %eax, %cr0
|
|
jmp wakeup_jmp
|
|
3:
|
|
/* Set up segments */
|
|
movw %cs, %ax
|
|
movw %ax, %ds
|
|
movw %ax, %es
|
|
movw %ax, %ss
|
|
lidtl wakeup_idt
|
|
|
|
movl $wakeup_stack_end, %esp
|
|
|
|
/* Clear the EFLAGS */
|
|
pushl $0
|
|
popfl
|
|
|
|
/* Check header signature... */
|
|
movl signature, %eax
|
|
cmpl $0x51ee1111, %eax
|
|
jne bogus_real_magic
|
|
|
|
/* Check we really have everything... */
|
|
movl end_signature, %eax
|
|
cmpl $0x65a22c82, %eax
|
|
jne bogus_real_magic
|
|
|
|
/* Call the C code */
|
|
calll main
|
|
|
|
/* Do any other stuff... */
|
|
|
|
#ifndef CONFIG_64BIT
|
|
/* This could also be done in C code... */
|
|
movl pmode_cr3, %eax
|
|
movl %eax, %cr3
|
|
|
|
movl pmode_cr4, %ecx
|
|
jecxz 1f
|
|
movl %ecx, %cr4
|
|
1:
|
|
movl pmode_efer, %eax
|
|
movl pmode_efer + 4, %edx
|
|
movl %eax, %ecx
|
|
orl %edx, %ecx
|
|
jz 1f
|
|
movl $0xc0000080, %ecx
|
|
wrmsr
|
|
1:
|
|
|
|
lgdtl pmode_gdt
|
|
|
|
/* This really couldn't... */
|
|
movl pmode_cr0, %eax
|
|
movl %eax, %cr0
|
|
jmp pmode_return
|
|
#else
|
|
pushw $0
|
|
pushw trampoline_segment
|
|
pushw $0
|
|
lret
|
|
#endif
|
|
|
|
bogus_real_magic:
|
|
1:
|
|
hlt
|
|
jmp 1b
|
|
|
|
.data
|
|
.balign 8
|
|
|
|
/* This is the standard real-mode IDT */
|
|
wakeup_idt:
|
|
.word 0xffff /* limit */
|
|
.long 0 /* address */
|
|
.word 0
|
|
|
|
.globl HEAP, heap_end
|
|
HEAP:
|
|
.long wakeup_heap
|
|
heap_end:
|
|
.long wakeup_stack
|
|
|
|
.bss
|
|
wakeup_heap:
|
|
.space 2048
|
|
wakeup_stack:
|
|
.space 2048
|
|
wakeup_stack_end:
|