Back to home page

OSCL-LXR

 
 

    


0001 /* SPDX-License-Identifier: GPL-2.0 */
0002 /*
0003  * ACPI wakeup real mode startup stub
0004  */
0005 #include <linux/linkage.h>
0006 #include <asm/segment.h>
0007 #include <asm/msr-index.h>
0008 #include <asm/page_types.h>
0009 #include <asm/pgtable_types.h>
0010 #include <asm/processor-flags.h>
0011 #include "realmode.h"
0012 #include "wakeup.h"
0013 
0014     .code16
0015 
0016 /* This should match the structure in wakeup.h */
0017     .section ".data", "aw"
0018 
0019     .balign 16
0020 SYM_DATA_START(wakeup_header)
0021     video_mode: .short  0   /* Video mode number */
0022     pmode_entry:    .long   0
0023     pmode_cs:   .short  __KERNEL_CS
0024     pmode_cr0:  .long   0   /* Saved %cr0 */
0025     pmode_cr3:  .long   0   /* Saved %cr3 */
0026     pmode_cr4:  .long   0   /* Saved %cr4 */
0027     pmode_efer: .quad   0   /* Saved EFER */
0028     pmode_gdt:  .quad   0
0029     pmode_misc_en:  .quad   0   /* Saved MISC_ENABLE MSR */
0030     pmode_behavior: .long   0   /* Wakeup behavior flags */
0031     realmode_flags: .long   0
0032     real_magic: .long   0
0033     signature:  .long   WAKEUP_HEADER_SIGNATURE
0034 SYM_DATA_END(wakeup_header)
0035 
0036     .text
0037     .code16
0038 
0039     .balign 16
0040 SYM_CODE_START(wakeup_start)
0041     cli
0042     cld
0043 
0044     LJMPW_RM(3f)
0045 3:
0046     /* Apparently some dimwit BIOS programmers don't know how to
0047        program a PM to RM transition, and we might end up here with
0048        junk in the data segment descriptor registers.  The only way
0049        to repair that is to go into PM and fix it ourselves... */
0050     movw    $16, %cx
0051     lgdtl   %cs:wakeup_gdt
0052     movl    %cr0, %eax
0053     orb $X86_CR0_PE, %al
0054     movl    %eax, %cr0
0055     ljmpw   $8, $2f
0056 2:
0057     movw    %cx, %ds
0058     movw    %cx, %es
0059     movw    %cx, %ss
0060     movw    %cx, %fs
0061     movw    %cx, %gs
0062 
0063     andb    $~X86_CR0_PE, %al
0064     movl    %eax, %cr0
0065     LJMPW_RM(3f)
0066 3:
0067     /* Set up segments */
0068     movw    %cs, %ax
0069     movw    %ax, %ss
0070     movl    $rm_stack_end, %esp
0071     movw    %ax, %ds
0072     movw    %ax, %es
0073     movw    %ax, %fs
0074     movw    %ax, %gs
0075 
0076     lidtl   .Lwakeup_idt
0077 
0078     /* Clear the EFLAGS */
0079     pushl $0
0080     popfl
0081 
0082     /* Check header signature... */
0083     movl    signature, %eax
0084     cmpl    $WAKEUP_HEADER_SIGNATURE, %eax
0085     jne bogus_real_magic
0086 
0087     /* Check we really have everything... */
0088     movl    end_signature, %eax
0089     cmpl    $REALMODE_END_SIGNATURE, %eax
0090     jne bogus_real_magic
0091 
0092     /* Call the C code */
0093     calll   main
0094 
0095     /* Restore MISC_ENABLE before entering protected mode, in case
0096        BIOS decided to clear XD_DISABLE during S3. */
0097     movl    pmode_behavior, %edi
0098     btl $WAKEUP_BEHAVIOR_RESTORE_MISC_ENABLE, %edi
0099     jnc 1f
0100 
0101     movl    pmode_misc_en, %eax
0102     movl    pmode_misc_en + 4, %edx
0103     movl    $MSR_IA32_MISC_ENABLE, %ecx
0104     wrmsr
0105 1:
0106 
0107     /* Do any other stuff... */
0108 
0109 #ifndef CONFIG_64BIT
0110     /* This could also be done in C code... */
0111     movl    pmode_cr3, %eax
0112     movl    %eax, %cr3
0113 
0114     btl $WAKEUP_BEHAVIOR_RESTORE_CR4, %edi
0115     jnc 1f
0116     movl    pmode_cr4, %eax
0117     movl    %eax, %cr4
0118 1:
0119     btl $WAKEUP_BEHAVIOR_RESTORE_EFER, %edi
0120     jnc 1f
0121     movl    pmode_efer, %eax
0122     movl    pmode_efer + 4, %edx
0123     movl    $MSR_EFER, %ecx
0124     wrmsr
0125 1:
0126 
0127     lgdtl   pmode_gdt
0128 
0129     /* This really couldn't... */
0130     movl    pmode_entry, %eax
0131     movl    pmode_cr0, %ecx
0132     movl    %ecx, %cr0
0133     ljmpl   $__KERNEL_CS, $pa_startup_32
0134     /* -> jmp *%eax in trampoline_32.S */
0135 #else
0136     jmp trampoline_start
0137 #endif
0138 SYM_CODE_END(wakeup_start)
0139 
0140 bogus_real_magic:
0141 1:
0142     hlt
0143     jmp 1b
0144 
0145     .section ".rodata","a"
0146 
0147     /*
0148      * Set up the wakeup GDT.  We set these up as Big Real Mode,
0149      * that is, with limits set to 4 GB.  At least the Lenovo
0150      * Thinkpad X61 is known to need this for the video BIOS
0151      * initialization quirk to work; this is likely to also
0152      * be the case for other laptops or integrated video devices.
0153      */
0154 
0155     .balign 16
0156 SYM_DATA_START(wakeup_gdt)
0157     .word   3*8-1       /* Self-descriptor */
0158     .long   pa_wakeup_gdt
0159     .word   0
0160 
0161     .word   0xffff      /* 16-bit code segment @ real_mode_base */
0162     .long   0x9b000000 + pa_real_mode_base
0163     .word   0x008f      /* big real mode */
0164 
0165     .word   0xffff      /* 16-bit data segment @ real_mode_base */
0166     .long   0x93000000 + pa_real_mode_base
0167     .word   0x008f      /* big real mode */
0168 SYM_DATA_END(wakeup_gdt)
0169 
0170     .section ".rodata","a"
0171     .balign 8
0172 
0173     /* This is the standard real-mode IDT */
0174     .balign 16
0175 SYM_DATA_START_LOCAL(.Lwakeup_idt)
0176     .word   0xffff      /* limit */
0177     .long   0       /* address */
0178     .word   0
0179 SYM_DATA_END(.Lwakeup_idt)