|  | /* | 
|  | * ACPI wakeup real mode startup stub | 
|  | */ | 
|  | #include <linux/linkage.h> | 
|  | #include <asm/segment.h> | 
|  | #include <asm/msr-index.h> | 
|  | #include <asm/page_types.h> | 
|  | #include <asm/pgtable_types.h> | 
|  | #include <asm/processor-flags.h> | 
|  | #include "realmode.h" | 
|  | #include "wakeup.h" | 
|  |  | 
|  | .code16 | 
|  |  | 
|  | /* This should match the structure in wakeup.h */ | 
|  | .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) | 
|  |  | 
|  | .text | 
|  | .code16 | 
|  |  | 
|  | .balign	16 | 
|  | ENTRY(wakeup_start) | 
|  | cli | 
|  | cld | 
|  |  | 
|  | LJMPW_RM(3f) | 
|  | 3: | 
|  | /* 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 | 
|  | 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 | 
|  | LJMPW_RM(3f) | 
|  | 3: | 
|  | /* Set up segments */ | 
|  | movw	%cs, %ax | 
|  | movw	%ax, %ss | 
|  | movl	$rm_stack_end, %esp | 
|  | movw	%ax, %ds | 
|  | movw	%ax, %es | 
|  | movw	%ax, %fs | 
|  | movw	%ax, %gs | 
|  |  | 
|  | lidtl	wakeup_idt | 
|  |  | 
|  | /* Clear the EFLAGS */ | 
|  | pushl $0 | 
|  | popfl | 
|  |  | 
|  | /* Check header signature... */ | 
|  | movl	signature, %eax | 
|  | cmpl	$WAKEUP_HEADER_SIGNATURE, %eax | 
|  | jne	bogus_real_magic | 
|  |  | 
|  | /* Check we really have everything... */ | 
|  | movl	end_signature, %eax | 
|  | cmpl	$REALMODE_END_SIGNATURE, %eax | 
|  | jne	bogus_real_magic | 
|  |  | 
|  | /* Call the C code */ | 
|  | calll	main | 
|  |  | 
|  | /* Restore MISC_ENABLE before entering protected mode, in case | 
|  | BIOS decided to clear XD_DISABLE during S3. */ | 
|  | movl	pmode_behavior, %edi | 
|  | btl	$WAKEUP_BEHAVIOR_RESTORE_MISC_ENABLE, %edi | 
|  | jnc	1f | 
|  |  | 
|  | movl	pmode_misc_en, %eax | 
|  | movl	pmode_misc_en + 4, %edx | 
|  | movl	$MSR_IA32_MISC_ENABLE, %ecx | 
|  | wrmsr | 
|  | 1: | 
|  |  | 
|  | /* Do any other stuff... */ | 
|  |  | 
|  | #ifndef CONFIG_64BIT | 
|  | /* This could also be done in C code... */ | 
|  | movl	pmode_cr3, %eax | 
|  | movl	%eax, %cr3 | 
|  |  | 
|  | btl	$WAKEUP_BEHAVIOR_RESTORE_CR4, %edi | 
|  | jnc	1f | 
|  | movl	pmode_cr4, %eax | 
|  | movl	%eax, %cr4 | 
|  | 1: | 
|  | btl	$WAKEUP_BEHAVIOR_RESTORE_EFER, %edi | 
|  | jnc	1f | 
|  | movl	pmode_efer, %eax | 
|  | movl	pmode_efer + 4, %edx | 
|  | movl	$MSR_EFER, %ecx | 
|  | wrmsr | 
|  | 1: | 
|  |  | 
|  | lgdtl	pmode_gdt | 
|  |  | 
|  | /* This really couldn't... */ | 
|  | movl	pmode_entry, %eax | 
|  | movl	pmode_cr0, %ecx | 
|  | movl	%ecx, %cr0 | 
|  | ljmpl	$__KERNEL_CS, $pa_startup_32 | 
|  | /* -> jmp *%eax in trampoline_32.S */ | 
|  | #else | 
|  | jmp	trampoline_start | 
|  | #endif | 
|  |  | 
|  | bogus_real_magic: | 
|  | 1: | 
|  | hlt | 
|  | jmp	1b | 
|  |  | 
|  | .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 | 
|  | GLOBAL(wakeup_gdt) | 
|  | .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 */ | 
|  | END(wakeup_gdt) | 
|  |  | 
|  | .section ".rodata","a" | 
|  | .balign	8 | 
|  |  | 
|  | /* This is the standard real-mode IDT */ | 
|  | .balign	16 | 
|  | GLOBAL(wakeup_idt) | 
|  | .word	0xffff		/* limit */ | 
|  | .long	0		/* address */ | 
|  | .word	0 | 
|  | END(wakeup_idt) |