forked from Minki/linux
84e70971e6
During the ACPI S3 suspend, we store the GDT in the wakup_header (see wakeup_asm.s) field called 'pmode_gdt'. Which is then used during the resume path and has the same exact value as what the store/load_gdt do with the saved_context (which is saved/restored via save/restore_processor_state()). The flow during resume from ACPI S3 is simpler than the 64-bit counterpart. We only use the early bootstrap once (wakeup_gdt) and do various checks in real mode. After the checks are completed, we load the saved GDT ('pmode_gdt') and continue on with the resume (by heading to startup_32 in trampoline_32.S) - which quickly jumps to what was saved in 'pmode_entry' aka 'wakeup_pmode_return'. The 'wakeup_pmode_return' restores the GDT (saved_gdt) again (which was saved in do_suspend_lowlevel initially). After that it ends up calling the 'ret_point' which calls 'restore_processor_state()'. We have two opportunities to remove code where we restore the same GDT twice. Here is the call chain: wakeup_start |- lgdtl wakeup_gdt [the work-around broken BIOSes] | | - lgdtl pmode_gdt [the real one] | \-- startup_32 (in trampoline_32.S) \-- wakeup_pmode_return (in wakeup_32.S) |- lgdtl saved_gdt [the real one] \-- ret_point |.. |- call restore_processor_state The hibernate path is much simpler. During the saving of the hibernation image we call save_processor_state() and save the contents of that along with the rest of the kernel in the hibernation image destination. We save the EIP of 'restore_registers' (restore_jump_address) and cr3 (restore_cr3). During hibernate resume, the 'restore_registers' (via the 'restore_jump_address) in hibernate_asm_32.S is invoked which restores the contents of most registers. Naturally the resume path benefits from already being in 32-bit mode, so it does not have to reload the GDT. It only reloads the cr3 (from restore_cr3) and continues on. Note that the restoration of the restore image page-tables is done prior to this. After the 'restore_registers' it returns and we end up called restore_processor_state() - where we reload the GDT. The reload of the GDT is not needed as bootup kernel has already loaded the GDT which is at the same physical location as the the restored kernel. Note that the hibernation path assumes the GDT is correct during its 'restore_registers'. The assumption in the code is that the restored image is the same as saved - meaning we are not trying to restore an different kernel in the virtual address space of a new kernel. Signed-off-by: Konrad Rzeszutek Wilk <konrad.wilk@oracle.com> Link: http://lkml.kernel.org/r/1365194544-14648-3-git-send-email-konrad.wilk@oracle.com Cc: Rafael J. Wysocki <rjw@sisk.pl> Signed-off-by: H. Peter Anvin <hpa@linux.intel.com>
98 lines
1.7 KiB
ArmAsm
98 lines
1.7 KiB
ArmAsm
.section .text..page_aligned
|
|
#include <linux/linkage.h>
|
|
#include <asm/segment.h>
|
|
#include <asm/page_types.h>
|
|
|
|
# Copyright 2003, 2008 Pavel Machek <pavel@suse.cz>, distribute under GPLv2
|
|
|
|
.code32
|
|
ALIGN
|
|
|
|
ENTRY(wakeup_pmode_return)
|
|
wakeup_pmode_return:
|
|
movw $__KERNEL_DS, %ax
|
|
movw %ax, %ss
|
|
movw %ax, %ds
|
|
movw %ax, %es
|
|
movw %ax, %fs
|
|
movw %ax, %gs
|
|
|
|
# reload the gdt, as we need the full 32 bit address
|
|
lidt saved_idt
|
|
lldt saved_ldt
|
|
ljmp $(__KERNEL_CS), $1f
|
|
1:
|
|
movl %cr3, %eax
|
|
movl %eax, %cr3
|
|
wbinvd
|
|
|
|
# and restore the stack ... but you need gdt for this to work
|
|
movl saved_context_esp, %esp
|
|
|
|
movl %cs:saved_magic, %eax
|
|
cmpl $0x12345678, %eax
|
|
jne bogus_magic
|
|
|
|
# jump to place where we left off
|
|
movl saved_eip, %eax
|
|
jmp *%eax
|
|
|
|
bogus_magic:
|
|
jmp bogus_magic
|
|
|
|
|
|
|
|
save_registers:
|
|
sidt saved_idt
|
|
sldt saved_ldt
|
|
str saved_tss
|
|
|
|
leal 4(%esp), %eax
|
|
movl %eax, saved_context_esp
|
|
movl %ebx, saved_context_ebx
|
|
movl %ebp, saved_context_ebp
|
|
movl %esi, saved_context_esi
|
|
movl %edi, saved_context_edi
|
|
pushfl
|
|
popl saved_context_eflags
|
|
|
|
movl $ret_point, saved_eip
|
|
ret
|
|
|
|
|
|
restore_registers:
|
|
movl saved_context_ebp, %ebp
|
|
movl saved_context_ebx, %ebx
|
|
movl saved_context_esi, %esi
|
|
movl saved_context_edi, %edi
|
|
pushl saved_context_eflags
|
|
popfl
|
|
ret
|
|
|
|
ENTRY(do_suspend_lowlevel)
|
|
call save_processor_state
|
|
call save_registers
|
|
pushl $3
|
|
call acpi_enter_sleep_state
|
|
addl $4, %esp
|
|
|
|
# In case of S3 failure, we'll emerge here. Jump
|
|
# to ret_point to recover
|
|
jmp ret_point
|
|
.p2align 4,,7
|
|
ret_point:
|
|
call restore_registers
|
|
call restore_processor_state
|
|
ret
|
|
|
|
.data
|
|
ALIGN
|
|
ENTRY(saved_magic) .long 0
|
|
ENTRY(saved_eip) .long 0
|
|
|
|
# saved registers
|
|
saved_idt: .long 0,0
|
|
saved_ldt: .long 0
|
|
saved_tss: .long 0
|
|
|