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