linux/arch/x86/realmode/rm/wakeup_asm.S
<<
>>
Prefs
   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
  20SYM_DATA_START(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
  34SYM_DATA_END(wakeup_header)
  35
  36        .text
  37        .code16
  38
  39        .balign 16
  40SYM_CODE_START(wakeup_start)
  41        cli
  42        cld
  43
  44        LJMPW_RM(3f)
  453:
  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
  562:
  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)
  663:
  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   .Lwakeup_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
 1051:
 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
 1181:
 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
 1251:
 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
 138SYM_CODE_END(wakeup_start)
 139
 140bogus_real_magic:
 1411:
 142        hlt
 143        jmp     1b
 144
 145        .section ".rodata","a"
 146
 147        /*
 148         * Set up the wakeup GDT.  We set these up as Big Real Mode,
 149         * that is, with limits set to 4 GB.  At least the Lenovo
 150         * Thinkpad X61 is known to need this for the video BIOS
 151         * initialization quirk to work; this is likely to also
 152         * be the case for other laptops or integrated video devices.
 153         */
 154
 155        .balign 16
 156SYM_DATA_START(wakeup_gdt)
 157        .word   3*8-1           /* Self-descriptor */
 158        .long   pa_wakeup_gdt
 159        .word   0
 160
 161        .word   0xffff          /* 16-bit code segment @ real_mode_base */
 162        .long   0x9b000000 + pa_real_mode_base
 163        .word   0x008f          /* big real mode */
 164
 165        .word   0xffff          /* 16-bit data segment @ real_mode_base */
 166        .long   0x93000000 + pa_real_mode_base
 167        .word   0x008f          /* big real mode */
 168SYM_DATA_END(wakeup_gdt)
 169
 170        .section ".rodata","a"
 171        .balign 8
 172
 173        /* This is the standard real-mode IDT */
 174        .balign 16
 175SYM_DATA_START_LOCAL(.Lwakeup_idt)
 176        .word   0xffff          /* limit */
 177        .long   0               /* address */
 178        .word   0
 179SYM_DATA_END(.Lwakeup_idt)
 180