summaryrefslogblamecommitdiffstats
path: root/arch/x86_64/kernel/acpi/wakeup.S
blob: bd4c6f1a6f329bbf30d0d9ae3454946d970988ce (plain) (tree)































                                                                                 
                                                                         
                        

                                                                                
 
                                                                  









                                              
                                                                          




















                                                                                   
                                                                           






                                                                         





                                                 

















                                                                        

                                  
 



                                                      


                                                   



                                           
                        

                          





                                                               
                              
                          
                                
 
                                   


                               

                                    

                               
             



                                                                                                  


                                    
 






                                                    






                                                                               
                                        
                                                            
 



                                                  


       


                                                                   








                                                                              


                                                  









                                            
                               

                                            



                               

                                            
                               





                        
                                                                   
                                                       



                                                                            





                                                               
                                                                 



                                                                            




                       
                                                 





                                                 





































































                                                                              
















                                                                          
               

                               

                    











                                                              

                                  































                                                
                                      
 




                           













































                                                             



                                 
 

                                 

                                 
.text
#include <linux/linkage.h>
#include <asm/segment.h>
#include <asm/page.h>
#include <asm/msr.h>

# Copyright 2003 Pavel Machek <pavel@suse.cz>, distribute under GPLv2
#
# wakeup_code runs in real mode, and at unknown address (determined at run-time).
# Therefore it must only use relative jumps/calls. 
#
# Do we need to deal with A20? It is okay: ACPI specs says A20 must be enabled
#
# If physical address of wakeup_code is 0x12345, BIOS should call us with
# cs = 0x1234, eip = 0x05
#


ALIGN
	.align	16
ENTRY(wakeup_start)
wakeup_code:
	wakeup_code_start = .
	.code16

# Running in *copy* of this code, somewhere in low 1MB.

	movb	$0xa1, %al	;  outb %al, $0x80
	cli
	cld
	# setup data segment
	movw	%cs, %ax
	movw	%ax, %ds		# Make ds:0 point to wakeup_start
	movw	%ax, %ss
					# Private stack is needed for ASUS board
	mov	$(wakeup_stack - wakeup_code), %sp

	pushl	$0			# Kill any dangerous flags
	popfl

	movl	real_magic - wakeup_code, %eax
	cmpl	$0x12345678, %eax
	jne	bogus_real_magic

	testl	$1, video_flags - wakeup_code
	jz	1f
	lcall   $0xc000,$3
	movw	%cs, %ax
	movw	%ax, %ds		# Bios might have played with that
	movw	%ax, %ss
1:

	testl	$2, video_flags - wakeup_code
	jz	1f
	mov	video_mode - wakeup_code, %ax
	call	mode_seta
1:

 	movw	$0xb800, %ax
	movw	%ax,%fs
	movw	$0x0e00 + 'L', %fs:(0x10)

	movb	$0xa2, %al	;  outb %al, $0x80
	
	lidt	%ds:idt_48a - wakeup_code
	xorl	%eax, %eax
	movw	%ds, %ax			# (Convert %ds:gdt to a linear ptr)
	shll	$4, %eax
	addl	$(gdta - wakeup_code), %eax
	movl	%eax, gdt_48a +2 - wakeup_code
	lgdtl	%ds:gdt_48a - wakeup_code	# load gdt with whatever is
						# appropriate

	movl	$1, %eax			# protected mode (PE) bit
	lmsw	%ax				# This is it!
	jmp	1f
1:

	ljmpl   *(wakeup_32_vector - wakeup_code)

	.balign 4
wakeup_32_vector:
	.long   wakeup_32 - __START_KERNEL_map
	.word   __KERNEL32_CS, 0

	.code32
wakeup_32:
# Running in this code, but at low address; paging is not yet turned on.
	movb	$0xa5, %al	;  outb %al, $0x80

	/* Check if extended functions are implemented */		
	movl	$0x80000000, %eax
	cpuid
	cmpl	$0x80000000, %eax
	jbe	bogus_cpu
	wbinvd
	mov	$0x80000001, %eax
	cpuid
	btl	$29, %edx
	jnc	bogus_cpu
	movl	%edx,%edi
	
	movl	$__KERNEL_DS, %eax
	movl	%eax, %ds

	movl	saved_magic - __START_KERNEL_map, %eax
	cmpl	$0x9abcdef0, %eax
	jne	bogus_32_magic

	movw	$0x0e00 + 'i', %ds:(0xb8012)
	movb	$0xa8, %al	;  outb %al, $0x80;

	/*
	 * Prepare for entering 64bits mode
	 */

	/* Enable PAE */
	xorl	%eax, %eax
	btsl	$5, %eax
	movl	%eax, %cr4

	/* Setup early boot stage 4 level pagetables */
	movl	$(wakeup_level4_pgt - __START_KERNEL_map), %eax
	movl	%eax, %cr3

	/* Enable Long Mode */
	xorl    %eax, %eax
	btsl	$_EFER_LME, %eax

	/* No Execute supported? */
	btl	$20,%edi
	jnc     1f
	btsl	$_EFER_NX, %eax
				
	/* Make changes effective */
1:	movl    $MSR_EFER, %ecx
	xorl    %edx, %edx
	wrmsr

	xorl	%eax, %eax
	btsl	$31, %eax			/* Enable paging and in turn activate Long Mode */
	btsl	$0, %eax			/* Enable protected mode */

	/* Make changes effective */
	movl	%eax, %cr0

	/* At this point:
		CR4.PAE must be 1
		CS.L must be 0
		CR3 must point to PML4
		Next instruction must be a branch
		This must be on identity-mapped page
	*/
	/*
	 * At this point we're in long mode but in 32bit compatibility mode
	 * with EFER.LME = 1, CS.L = 0, CS.D = 1 (and in turn
	 * EFER.LMA = 1). Now we want to jump in 64bit mode, to do that we load
	 * the new gdt/idt that has __KERNEL_CS with CS.L = 1.
	 */

	/* Finally jump in 64bit mode */
	ljmp	*(wakeup_long64_vector - __START_KERNEL_map)

	.balign 4
wakeup_long64_vector:
	.long   wakeup_long64 - __START_KERNEL_map
	.word   __KERNEL_CS, 0

.code64

	/* Hooray, we are in Long 64-bit mode (but still running in
	 * low memory)
	 */
wakeup_long64:
	/*
	 * We must switch to a new descriptor in kernel space for the GDT
	 * because soon the kernel won't have access anymore to the userspace
	 * addresses where we're currently running on. We have to do that here
	 * because in 32bit we couldn't load a 64bit linear address.
	 */
	lgdt	cpu_gdt_descr - __START_KERNEL_map

	movw	$0x0e00 + 'n', %ds:(0xb8014)
	movb	$0xa9, %al	;  outb %al, $0x80

	movw	$0x0e00 + 'u', %ds:(0xb8016)
	
	nop
	nop
	movw	$__KERNEL_DS, %ax
	movw	%ax, %ss	
	movw	%ax, %ds
	movw	%ax, %es
	movw	%ax, %fs
	movw	%ax, %gs
	movq	saved_rsp, %rsp

	movw	$0x0e00 + 'x', %ds:(0xb8018)
	movq	saved_rbx, %rbx
	movq	saved_rdi, %rdi
	movq	saved_rsi, %rsi
	movq	saved_rbp, %rbp

	movw	$0x0e00 + '!', %ds:(0xb801a)
	movq	saved_rip, %rax
	jmp	*%rax

.code32

	.align	64	
gdta:
	/* Its good to keep gdt in sync with one in trampoline.S */
	.word	0, 0, 0, 0			# dummy
	/* ??? Why I need the accessed bit set in order for this to work? */
	.quad   0x00cf9b000000ffff              # __KERNEL32_CS
	.quad   0x00af9b000000ffff              # __KERNEL_CS
	.quad   0x00cf93000000ffff              # __KERNEL_DS

idt_48a:
	.word	0				# idt limit = 0
	.word	0, 0				# idt base = 0L

gdt_48a:
	.word	0x800				# gdt limit=2048,
						#  256 GDT entries
	.word	0, 0				# gdt base (filled in later)
	
	
real_magic:	.quad 0
video_mode:	.quad 0
video_flags:	.quad 0

bogus_real_magic:
	movb	$0xba,%al	;  outb %al,$0x80
	jmp bogus_real_magic

bogus_32_magic:
	movb	$0xb3,%al	;  outb %al,$0x80
	jmp bogus_32_magic

bogus_cpu:
	movb	$0xbc,%al	;  outb %al,$0x80
	jmp bogus_cpu

	
/* This code uses an extended set of video mode numbers. These include:
 * Aliases for standard modes
 *	NORMAL_VGA (-1)
 *	EXTENDED_VGA (-2)
 *	ASK_VGA (-3)
 * Video modes numbered by menu position -- NOT RECOMMENDED because of lack
 * of compatibility when extending the table. These are between 0x00 and 0xff.
 */
#define VIDEO_FIRST_MENU 0x0000

/* Standard BIOS video modes (BIOS number + 0x0100) */
#define VIDEO_FIRST_BIOS 0x0100

/* VESA BIOS video modes (VESA number + 0x0200) */
#define VIDEO_FIRST_VESA 0x0200

/* Video7 special modes (BIOS number + 0x0900) */
#define VIDEO_FIRST_V7 0x0900

# Setting of user mode (AX=mode ID) => CF=success
mode_seta:
	movw	%ax, %bx
#if 0
	cmpb	$0xff, %ah
	jz	setalias

	testb	$VIDEO_RECALC>>8, %ah
	jnz	_setrec

	cmpb	$VIDEO_FIRST_RESOLUTION>>8, %ah
	jnc	setres
	
	cmpb	$VIDEO_FIRST_SPECIAL>>8, %ah
	jz	setspc

	cmpb	$VIDEO_FIRST_V7>>8, %ah
	jz	setv7
#endif
	
	cmpb	$VIDEO_FIRST_VESA>>8, %ah
	jnc	check_vesaa
#if 0	
	orb	%ah, %ah
	jz	setmenu
#endif
	
	decb	%ah
#	jz	setbios				  Add bios modes later

setbada:	clc
	ret

check_vesaa:
	subb	$VIDEO_FIRST_VESA>>8, %bh
	orw	$0x4000, %bx			# Use linear frame buffer
	movw	$0x4f02, %ax			# VESA BIOS mode set call
	int	$0x10
	cmpw	$0x004f, %ax			# AL=4f if implemented
	jnz	_setbada				# AH=0 if OK

	stc
	ret

_setbada: jmp setbada

wakeup_stack_begin:	# Stack grows down

.org	0xff0
wakeup_stack:		# Just below end of page

ENTRY(wakeup_end)
	
##
# acpi_copy_wakeup_routine
#
# Copy the above routine to low memory.
#
# Parameters:
# %rdi:	place to copy wakeup routine to
#
# Returned address is location of code in low memory (past data and stack)
#
	.code64
ENTRY(acpi_copy_wakeup_routine)
	pushq	%rax
	pushq	%rdx

	movl	saved_video_mode, %edx
	movl	%edx, video_mode - wakeup_start (,%rdi)
	movl	acpi_video_flags, %edx
	movl	%edx, video_flags - wakeup_start (,%rdi)
	movq	$0x12345678, real_magic - wakeup_start (,%rdi)
	movq	$0x123456789abcdef0, %rdx
	movq	%rdx, saved_magic

	movl	saved_magic - __START_KERNEL_map, %eax
	cmpl	$0x9abcdef0, %eax
	jne	bogus_32_magic

	# restore the regs we used
	popq	%rdx
	popq	%rax
ENTRY(do_suspend_lowlevel_s4bios)
	ret

	.align 2
	.p2align 4,,15
.globl do_suspend_lowlevel
	.type	do_suspend_lowlevel,@function
do_suspend_lowlevel:
.LFB5:
	subq	$8, %rsp
	xorl	%eax, %eax
	call	save_processor_state

	movq %rsp, saved_context_esp(%rip)
	movq %rax, saved_context_eax(%rip)
	movq %rbx, saved_context_ebx(%rip)
	movq %rcx, saved_context_ecx(%rip)
	movq %rdx, saved_context_edx(%rip)
	movq %rbp, saved_context_ebp(%rip)
	movq %rsi, saved_context_esi(%rip)
	movq %rdi, saved_context_edi(%rip)
	movq %r8,  saved_context_r08(%rip)
	movq %r9,  saved_context_r09(%rip)
	movq %r10, saved_context_r10(%rip)
	movq %r11, saved_context_r11(%rip)
	movq %r12, saved_context_r12(%rip)
	movq %r13, saved_context_r13(%rip)
	movq %r14, saved_context_r14(%rip)
	movq %r15, saved_context_r15(%rip)
	pushfq ; popq saved_context_eflags(%rip)

	movq	$.L97, saved_rip(%rip)

	movq %rsp,saved_rsp
	movq %rbp,saved_rbp
	movq %rbx,saved_rbx
	movq %rdi,saved_rdi
	movq %rsi,saved_rsi

	addq	$8, %rsp
	movl	$3, %edi
	xorl	%eax, %eax
	jmp	acpi_enter_sleep_state
.L97:
	.p2align 4,,7
.L99:
	.align 4
	movl	$24, %eax
	movw %ax, %ds
	movq	saved_context+58(%rip), %rax
	movq %rax, %cr4
	movq	saved_context+50(%rip), %rax
	movq %rax, %cr3
	movq	saved_context+42(%rip), %rax
	movq %rax, %cr2
	movq	saved_context+34(%rip), %rax
	movq %rax, %cr0
	pushq saved_context_eflags(%rip) ; popfq
	movq saved_context_esp(%rip), %rsp
	movq saved_context_ebp(%rip), %rbp
	movq saved_context_eax(%rip), %rax
	movq saved_context_ebx(%rip), %rbx
	movq saved_context_ecx(%rip), %rcx
	movq saved_context_edx(%rip), %rdx
	movq saved_context_esi(%rip), %rsi
	movq saved_context_edi(%rip), %rdi
	movq saved_context_r08(%rip), %r8
	movq saved_context_r09(%rip), %r9
	movq saved_context_r10(%rip), %r10
	movq saved_context_r11(%rip), %r11
	movq saved_context_r12(%rip), %r12
	movq saved_context_r13(%rip), %r13
	movq saved_context_r14(%rip), %r14
	movq saved_context_r15(%rip), %r15

	xorl	%eax, %eax
	addq	$8, %rsp
	jmp	restore_processor_state
.LFE5:
.Lfe5:
	.size	do_suspend_lowlevel,.Lfe5-do_suspend_lowlevel
	
.data
ALIGN
ENTRY(saved_rbp)	.quad	0
ENTRY(saved_rsi)	.quad	0
ENTRY(saved_rdi)	.quad	0
ENTRY(saved_rbx)	.quad	0

ENTRY(saved_rip)	.quad	0
ENTRY(saved_rsp)	.quad	0

ENTRY(saved_magic)	.quad	0