| 
									
										
										
										
											2008-04-10 23:28:10 +02:00
										 |  |  | /* | 
					
						
							|  |  |  |  * ACPI wakeup real mode startup stub | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2012-05-08 21:22:40 +03:00
										 |  |  | #include <linux/linkage.h> | 
					
						
							| 
									
										
										
										
											2008-04-10 23:28:10 +02:00
										 |  |  | #include <asm/segment.h> | 
					
						
							|  |  |  | #include <asm/msr-index.h> | 
					
						
							| 
									
										
										
										
											2009-02-13 11:14:01 -08:00
										 |  |  | #include <asm/page_types.h> | 
					
						
							|  |  |  | #include <asm/pgtable_types.h> | 
					
						
							| 
									
										
										
										
											2008-06-24 23:03:48 +02:00
										 |  |  | #include <asm/processor-flags.h> | 
					
						
							| 
									
										
										
										
											2012-05-08 21:22:42 +03:00
										 |  |  | #include "realmode.h" | 
					
						
							| 
									
										
										
										
											2011-02-14 15:42:46 -08:00
										 |  |  | #include "wakeup.h" | 
					
						
							| 
									
										
										
										
											2008-04-10 23:28:10 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-05-08 21:22:40 +03:00
										 |  |  | 	.code16 | 
					
						
							| 
									
										
										
										
											2008-04-10 23:28:10 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | /* This should match the structure in wakeup.h */ | 
					
						
							| 
									
										
										
										
											2012-05-08 21:22:40 +03:00
										 |  |  | 	.section ".data", "aw" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	.balign	16
 | 
					
						
							|  |  |  | GLOBAL(wakeup_header) | 
					
						
							|  |  |  | 	video_mode:	.short	0	/* Video mode number */ | 
					
						
							|  |  |  | 	pmode_entry:	.long	0 | 
					
						
							|  |  |  | 	pmode_cs:	.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 | 
					
						
							|  |  |  | 	pmode_misc_en:	.quad	0	/* Saved MISC_ENABLE MSR */ | 
					
						
							|  |  |  | 	pmode_behavior:	.long	0	/* Wakeup behavior flags */ | 
					
						
							|  |  |  | 	realmode_flags:	.long	0 | 
					
						
							|  |  |  | 	real_magic:	.long	0 | 
					
						
							|  |  |  | 	signature:	.long	WAKEUP_HEADER_SIGNATURE | 
					
						
							|  |  |  | END(wakeup_header) | 
					
						
							| 
									
										
										
										
											2008-04-10 23:28:10 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	.text | 
					
						
							|  |  |  | 	.code16 | 
					
						
							| 
									
										
										
										
											2012-05-08 21:22:40 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	.balign	16
 | 
					
						
							|  |  |  | ENTRY(wakeup_start) | 
					
						
							| 
									
										
										
										
											2012-05-08 21:22:29 +03:00
										 |  |  | 	cli | 
					
						
							| 
									
										
										
										
											2008-04-10 23:28:10 +02:00
										 |  |  | 	cld | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-05-08 21:22:37 +03:00
										 |  |  | 	LJMPW_RM(3f) | 
					
						
							| 
									
										
										
										
											2012-05-08 21:22:29 +03:00
										 |  |  | 3: | 
					
						
							| 
									
										
										
										
											2008-06-24 23:03:48 +02:00
										 |  |  | 	/* 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 | 
					
						
							| 
									
										
										
										
											2012-05-08 21:22:29 +03:00
										 |  |  | 	ljmpw	$8, $2f | 
					
						
							| 
									
										
										
										
											2008-06-24 23:03:48 +02:00
										 |  |  | 2: | 
					
						
							|  |  |  | 	movw	%cx, %ds | 
					
						
							|  |  |  | 	movw	%cx, %es | 
					
						
							|  |  |  | 	movw	%cx, %ss | 
					
						
							|  |  |  | 	movw	%cx, %fs | 
					
						
							|  |  |  | 	movw	%cx, %gs | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	andb	$~X86_CR0_PE, %al | 
					
						
							|  |  |  | 	movl	%eax, %cr0 | 
					
						
							| 
									
										
										
										
											2012-05-08 21:22:37 +03:00
										 |  |  | 	LJMPW_RM(3f) | 
					
						
							| 
									
										
										
										
											2008-06-24 23:03:48 +02:00
										 |  |  | 3: | 
					
						
							| 
									
										
										
										
											2008-04-10 23:28:10 +02:00
										 |  |  | 	/* Set up segments */ | 
					
						
							|  |  |  | 	movw	%cs, %ax | 
					
						
							| 
									
										
										
										
											2012-05-08 21:22:40 +03:00
										 |  |  | 	movw	%ax, %ss | 
					
						
							|  |  |  | 	movl	$rm_stack_end, %esp | 
					
						
							| 
									
										
										
										
											2008-04-10 23:28:10 +02:00
										 |  |  | 	movw	%ax, %ds | 
					
						
							|  |  |  | 	movw	%ax, %es | 
					
						
							| 
									
										
										
										
											2012-05-08 21:22:40 +03:00
										 |  |  | 	movw	%ax, %fs | 
					
						
							|  |  |  | 	movw	%ax, %gs | 
					
						
							| 
									
										
										
										
											2008-04-10 23:28:10 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-05-08 21:22:40 +03:00
										 |  |  | 	lidtl	wakeup_idt | 
					
						
							| 
									
										
										
										
											2008-04-10 23:28:10 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-10-01 14:34:42 -07:00
										 |  |  | 	/* Clear the EFLAGS */ | 
					
						
							| 
									
										
										
										
											2012-09-26 15:02:34 -07:00
										 |  |  | 	pushl $0 | 
					
						
							|  |  |  | 	popfl | 
					
						
							| 
									
										
										
										
											2008-04-10 23:28:10 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	/* Check header signature... */ | 
					
						
							|  |  |  | 	movl	signature, %eax | 
					
						
							| 
									
										
										
										
											2011-02-14 15:42:46 -08:00
										 |  |  | 	cmpl	$WAKEUP_HEADER_SIGNATURE, %eax | 
					
						
							| 
									
										
										
										
											2008-04-10 23:28:10 +02:00
										 |  |  | 	jne	bogus_real_magic | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Check we really have everything... */ | 
					
						
							|  |  |  | 	movl	end_signature, %eax | 
					
						
							| 
									
										
										
										
											2012-05-21 00:02:45 -07:00
										 |  |  | 	cmpl	$REALMODE_END_SIGNATURE, %eax | 
					
						
							| 
									
										
										
										
											2008-04-10 23:28:10 +02:00
										 |  |  | 	jne	bogus_real_magic | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Call the C code */ | 
					
						
							|  |  |  | 	calll	main | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
											  
											
												x86, suspend: Restore MISC_ENABLE MSR in realmode wakeup
Some BIOSes will reset the Intel MISC_ENABLE MSR (specifically the
XD_DISABLE bit) when resuming from S3, which can interact poorly with
ebba638ae723d8a8fc2f7abce5ec18b688b791d7. In 32bit PAE mode, this can
lead to a fault when EFER is restored by the kernel wakeup routines,
due to it setting the NX bit for a CPU that (thanks to the BIOS reset)
now incorrectly thinks it lacks the NX feature. (64bit is not affected
because it uses a common CPU bring-up that specifically handles the
XD_DISABLE bit.)
The need for MISC_ENABLE being restored so early is specific to the S3
resume path. Normally, MISC_ENABLE is saved in save_processor_state(),
but this happens after the resume header is created, so just reproduce
the logic here. (acpi_suspend_lowlevel() creates the header, calls
do_suspend_lowlevel, which calls save_processor_state(), so the saved
processor context isn't available during resume header creation.)
[ hpa: Consider for stable if OK in mainline ]
Signed-off-by: Kees Cook <kees.cook@canonical.com>
Link: http://lkml.kernel.org/r/20110707011034.GA8523@outflux.net
Signed-off-by: H. Peter Anvin <hpa@zytor.com>
Cc: Rafael J. Wysocki <rjw@sisk.pl>
Cc: <stable@kernel.org> 2.6.38+
											
										 
											2011-07-06 18:10:34 -07:00
										 |  |  | 	/* Restore MISC_ENABLE before entering protected mode, in case | 
					
						
							|  |  |  | 	   BIOS decided to clear XD_DISABLE during S3. */ | 
					
						
							| 
									
										
										
										
											2012-09-26 15:02:34 -07:00
										 |  |  | 	movl	pmode_behavior, %edi | 
					
						
							|  |  |  | 	btl	$WAKEUP_BEHAVIOR_RESTORE_MISC_ENABLE, %edi | 
					
						
							| 
									
										
											  
											
												x86, suspend: Restore MISC_ENABLE MSR in realmode wakeup
Some BIOSes will reset the Intel MISC_ENABLE MSR (specifically the
XD_DISABLE bit) when resuming from S3, which can interact poorly with
ebba638ae723d8a8fc2f7abce5ec18b688b791d7. In 32bit PAE mode, this can
lead to a fault when EFER is restored by the kernel wakeup routines,
due to it setting the NX bit for a CPU that (thanks to the BIOS reset)
now incorrectly thinks it lacks the NX feature. (64bit is not affected
because it uses a common CPU bring-up that specifically handles the
XD_DISABLE bit.)
The need for MISC_ENABLE being restored so early is specific to the S3
resume path. Normally, MISC_ENABLE is saved in save_processor_state(),
but this happens after the resume header is created, so just reproduce
the logic here. (acpi_suspend_lowlevel() creates the header, calls
do_suspend_lowlevel, which calls save_processor_state(), so the saved
processor context isn't available during resume header creation.)
[ hpa: Consider for stable if OK in mainline ]
Signed-off-by: Kees Cook <kees.cook@canonical.com>
Link: http://lkml.kernel.org/r/20110707011034.GA8523@outflux.net
Signed-off-by: H. Peter Anvin <hpa@zytor.com>
Cc: Rafael J. Wysocki <rjw@sisk.pl>
Cc: <stable@kernel.org> 2.6.38+
											
										 
											2011-07-06 18:10:34 -07:00
										 |  |  | 	jnc	1f | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	movl	pmode_misc_en, %eax | 
					
						
							|  |  |  | 	movl	pmode_misc_en + 4, %edx | 
					
						
							|  |  |  | 	movl	$MSR_IA32_MISC_ENABLE, %ecx | 
					
						
							|  |  |  | 	wrmsr | 
					
						
							|  |  |  | 1: | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-04-10 23:28:10 +02:00
										 |  |  | 	/* Do any other stuff... */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #ifndef CONFIG_64BIT | 
					
						
							|  |  |  | 	/* This could also be done in C code... */ | 
					
						
							|  |  |  | 	movl	pmode_cr3, %eax | 
					
						
							|  |  |  | 	movl	%eax, %cr3 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-09-26 15:02:34 -07:00
										 |  |  | 	btl	$WAKEUP_BEHAVIOR_RESTORE_CR4, %edi | 
					
						
							| 
									
										
										
										
											2012-10-01 14:34:42 -07:00
										 |  |  | 	jnc	1f | 
					
						
							| 
									
										
										
										
											2012-09-26 15:02:34 -07:00
										 |  |  | 	movl	pmode_cr4, %eax | 
					
						
							|  |  |  | 	movl	%eax, %cr4 | 
					
						
							| 
									
										
										
										
											2008-04-10 23:28:10 +02:00
										 |  |  | 1: | 
					
						
							| 
									
										
										
										
											2012-09-26 15:02:34 -07:00
										 |  |  | 	btl	$WAKEUP_BEHAVIOR_RESTORE_EFER, %edi | 
					
						
							| 
									
										
										
										
											2012-10-01 14:34:42 -07:00
										 |  |  | 	jnc	1f | 
					
						
							| 
									
										
										
										
											2008-04-10 23:28:10 +02:00
										 |  |  | 	movl	pmode_efer, %eax | 
					
						
							|  |  |  | 	movl	pmode_efer + 4, %edx | 
					
						
							| 
									
										
										
										
											2010-07-17 09:03:27 -04:00
										 |  |  | 	movl	$MSR_EFER, %ecx | 
					
						
							| 
									
										
										
										
											2008-04-10 23:28:10 +02:00
										 |  |  | 	wrmsr | 
					
						
							|  |  |  | 1: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	lgdtl	pmode_gdt | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* This really couldn't... */ | 
					
						
							| 
									
										
										
										
											2012-05-08 21:22:36 +03:00
										 |  |  | 	movl	pmode_entry, %eax | 
					
						
							|  |  |  | 	movl	pmode_cr0, %ecx | 
					
						
							|  |  |  | 	movl	%ecx, %cr0 | 
					
						
							|  |  |  | 	ljmpl	$__KERNEL_CS, $pa_startup_32 | 
					
						
							|  |  |  | 	/* -> jmp *%eax in trampoline_32.S */ | 
					
						
							| 
									
										
										
										
											2008-04-10 23:28:10 +02:00
										 |  |  | #else | 
					
						
							| 
									
										
										
										
											2012-05-08 21:22:43 +03:00
										 |  |  | 	jmp	trampoline_start | 
					
						
							| 
									
										
										
										
											2008-04-10 23:28:10 +02:00
										 |  |  | #endif | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | bogus_real_magic: | 
					
						
							|  |  |  | 1: | 
					
						
							|  |  |  | 	hlt | 
					
						
							|  |  |  | 	jmp	1b | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-05-08 21:22:29 +03:00
										 |  |  | 	.section ".rodata","a" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* | 
					
						
							|  |  |  | 	 * Set up the wakeup GDT.  We set these up as Big Real Mode, | 
					
						
							|  |  |  | 	 * that is, with limits set to 4 GB.  At least the Lenovo | 
					
						
							|  |  |  | 	 * Thinkpad X61 is known to need this for the video BIOS | 
					
						
							|  |  |  | 	 * initialization quirk to work; this is likely to also
 | 
					
						
							|  |  |  | 	 * be the case for other laptops or integrated video devices. | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	.balign	16
 | 
					
						
							| 
									
										
										
										
											2012-05-08 21:22:40 +03:00
										 |  |  | GLOBAL(wakeup_gdt) | 
					
						
							| 
									
										
										
										
											2012-05-08 21:22:29 +03:00
										 |  |  | 	.word	3*8-1		/* Self-descriptor */ | 
					
						
							|  |  |  | 	.long	pa_wakeup_gdt
 | 
					
						
							|  |  |  | 	.word	0
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	.word	0xffff		/* 16-bit code segment @ real_mode_base */ | 
					
						
							|  |  |  | 	.long	0x9b000000 + pa_real_mode_base | 
					
						
							|  |  |  | 	.word	0x008f		/* big real mode */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	.word	0xffff		/* 16-bit data segment @ real_mode_base */ | 
					
						
							|  |  |  | 	.long	0x93000000 + pa_real_mode_base | 
					
						
							|  |  |  | 	.word	0x008f		/* big real mode */ | 
					
						
							| 
									
										
										
										
											2012-05-08 21:22:40 +03:00
										 |  |  | END(wakeup_gdt) | 
					
						
							| 
									
										
										
										
											2012-05-08 21:22:29 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-05-08 21:22:40 +03:00
										 |  |  | 	.section ".rodata","a" | 
					
						
							| 
									
										
										
										
											2008-06-24 23:03:48 +02:00
										 |  |  | 	.balign	8
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* This is the standard real-mode IDT */ | 
					
						
							| 
									
										
										
										
											2012-05-08 21:22:40 +03:00
										 |  |  | 	.balign	16
 | 
					
						
							|  |  |  | GLOBAL(wakeup_idt) | 
					
						
							| 
									
										
										
										
											2008-06-24 23:03:48 +02:00
										 |  |  | 	.word	0xffff		/* limit */ | 
					
						
							|  |  |  | 	.long	0		/* address */ | 
					
						
							|  |  |  | 	.word	0
 | 
					
						
							| 
									
										
										
										
											2012-05-08 21:22:40 +03:00
										 |  |  | END(wakeup_idt) |