root/arch/x86/realmode/rm/wakeup_asm.S

/* [<][>][^][v][top][bottom][index][help] */
   1 /* SPDX-License-Identifier: GPL-2.0 */
   2 /*
   3  * ACPI wakeup real mode startup stub
   4  */
   5 #include <linux/linkage.h>
   6 #include <asm/segment.h>
   7 #include <asm/msr-index.h>
   8 #include <asm/page_types.h>
   9 #include <asm/pgtable_types.h>
  10 #include <asm/processor-flags.h>
  11 #include "realmode.h"
  12 #include "wakeup.h"
  13 
  14         .code16
  15 
  16 /* This should match the structure in wakeup.h */
  17         .section ".data", "aw"
  18 
  19         .balign 16
  20 GLOBAL(wakeup_header)
  21         video_mode:     .short  0       /* Video mode number */
  22         pmode_entry:    .long   0
  23         pmode_cs:       .short  __KERNEL_CS
  24         pmode_cr0:      .long   0       /* Saved %cr0 */
  25         pmode_cr3:      .long   0       /* Saved %cr3 */
  26         pmode_cr4:      .long   0       /* Saved %cr4 */
  27         pmode_efer:     .quad   0       /* Saved EFER */
  28         pmode_gdt:      .quad   0
  29         pmode_misc_en:  .quad   0       /* Saved MISC_ENABLE MSR */
  30         pmode_behavior: .long   0       /* Wakeup behavior flags */
  31         realmode_flags: .long   0
  32         real_magic:     .long   0
  33         signature:      .long   WAKEUP_HEADER_SIGNATURE
  34 END(wakeup_header)
  35 
  36         .text
  37         .code16
  38 
  39         .balign 16
  40 ENTRY(wakeup_start)
  41         cli
  42         cld
  43 
  44         LJMPW_RM(3f)
  45 3:
  46         /* Apparently some dimwit BIOS programmers don't know how to
  47            program a PM to RM transition, and we might end up here with
  48            junk in the data segment descriptor registers.  The only way
  49            to repair that is to go into PM and fix it ourselves... */
  50         movw    $16, %cx
  51         lgdtl   %cs:wakeup_gdt
  52         movl    %cr0, %eax
  53         orb     $X86_CR0_PE, %al
  54         movl    %eax, %cr0
  55         ljmpw   $8, $2f
  56 2:
  57         movw    %cx, %ds
  58         movw    %cx, %es
  59         movw    %cx, %ss
  60         movw    %cx, %fs
  61         movw    %cx, %gs
  62 
  63         andb    $~X86_CR0_PE, %al
  64         movl    %eax, %cr0
  65         LJMPW_RM(3f)
  66 3:
  67         /* Set up segments */
  68         movw    %cs, %ax
  69         movw    %ax, %ss
  70         movl    $rm_stack_end, %esp
  71         movw    %ax, %ds
  72         movw    %ax, %es
  73         movw    %ax, %fs
  74         movw    %ax, %gs
  75 
  76         lidtl   wakeup_idt
  77 
  78         /* Clear the EFLAGS */
  79         pushl $0
  80         popfl
  81 
  82         /* Check header signature... */
  83         movl    signature, %eax
  84         cmpl    $WAKEUP_HEADER_SIGNATURE, %eax
  85         jne     bogus_real_magic
  86 
  87         /* Check we really have everything... */
  88         movl    end_signature, %eax
  89         cmpl    $REALMODE_END_SIGNATURE, %eax
  90         jne     bogus_real_magic
  91 
  92         /* Call the C code */
  93         calll   main
  94 
  95         /* Restore MISC_ENABLE before entering protected mode, in case
  96            BIOS decided to clear XD_DISABLE during S3. */
  97         movl    pmode_behavior, %edi
  98         btl     $WAKEUP_BEHAVIOR_RESTORE_MISC_ENABLE, %edi
  99         jnc     1f
 100 
 101         movl    pmode_misc_en, %eax
 102         movl    pmode_misc_en + 4, %edx
 103         movl    $MSR_IA32_MISC_ENABLE, %ecx
 104         wrmsr
 105 1:
 106 
 107         /* Do any other stuff... */
 108 
 109 #ifndef CONFIG_64BIT
 110         /* This could also be done in C code... */
 111         movl    pmode_cr3, %eax
 112         movl    %eax, %cr3
 113 
 114         btl     $WAKEUP_BEHAVIOR_RESTORE_CR4, %edi
 115         jnc     1f
 116         movl    pmode_cr4, %eax
 117         movl    %eax, %cr4
 118 1:
 119         btl     $WAKEUP_BEHAVIOR_RESTORE_EFER, %edi
 120         jnc     1f
 121         movl    pmode_efer, %eax
 122         movl    pmode_efer + 4, %edx
 123         movl    $MSR_EFER, %ecx
 124         wrmsr
 125 1:
 126 
 127         lgdtl   pmode_gdt
 128 
 129         /* This really couldn't... */
 130         movl    pmode_entry, %eax
 131         movl    pmode_cr0, %ecx
 132         movl    %ecx, %cr0
 133         ljmpl   $__KERNEL_CS, $pa_startup_32
 134         /* -> jmp *%eax in trampoline_32.S */
 135 #else
 136         jmp     trampoline_start
 137 #endif
 138 
 139 bogus_real_magic:
 140 1:
 141         hlt
 142         jmp     1b
 143 
 144         .section ".rodata","a"
 145 
 146         /*
 147          * Set up the wakeup GDT.  We set these up as Big Real Mode,
 148          * that is, with limits set to 4 GB.  At least the Lenovo
 149          * Thinkpad X61 is known to need this for the video BIOS
 150          * initialization quirk to work; this is likely to also
 151          * be the case for other laptops or integrated video devices.
 152          */
 153 
 154         .balign 16
 155 GLOBAL(wakeup_gdt)
 156         .word   3*8-1           /* Self-descriptor */
 157         .long   pa_wakeup_gdt
 158         .word   0
 159 
 160         .word   0xffff          /* 16-bit code segment @ real_mode_base */
 161         .long   0x9b000000 + pa_real_mode_base
 162         .word   0x008f          /* big real mode */
 163 
 164         .word   0xffff          /* 16-bit data segment @ real_mode_base */
 165         .long   0x93000000 + pa_real_mode_base
 166         .word   0x008f          /* big real mode */
 167 END(wakeup_gdt)
 168 
 169         .section ".rodata","a"
 170         .balign 8
 171 
 172         /* This is the standard real-mode IDT */
 173         .balign 16
 174 GLOBAL(wakeup_idt)
 175         .word   0xffff          /* limit */
 176         .long   0               /* address */
 177         .word   0
 178 END(wakeup_idt)

/* [<][>][^][v][top][bottom][index][help] */