Home | History | Annotate | Download | only in X64
      1 #------------------------------------------------------------------------------
      2 #
      3 # Copyright (c) 2006 - 2013, Intel Corporation. All rights reserved.<BR>
      4 # This program and the accompanying materials
      5 # are licensed and made available under the terms and conditions of the BSD License
      6 # which accompanies this distribution.  The full text of the license may be found at
      7 # http://opensource.org/licenses/bsd-license.php.
      8 #
      9 # THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
     10 # WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
     11 #
     12 # Module Name:
     13 #
     14 #   Thunk16.S
     15 #
     16 # Abstract:
     17 #
     18 #   Real mode thunk
     19 #
     20 #------------------------------------------------------------------------------
     21 
     22 #include <Library/BaseLib.h>
     23 
     24 ASM_GLOBAL ASM_PFX(m16Start)
     25 ASM_GLOBAL ASM_PFX(m16Size)
     26 ASM_GLOBAL ASM_PFX(mThunk16Attr)
     27 ASM_GLOBAL ASM_PFX(m16Gdt)
     28 ASM_GLOBAL ASM_PFX(m16GdtrBase)
     29 ASM_GLOBAL ASM_PFX(mTransition)
     30 ASM_GLOBAL ASM_PFX(InternalAsmThunk16)
     31 
     32 # define the structure of IA32_REGS
     33 .set  _EDI, 0       #size 4
     34 .set  _ESI, 4       #size 4
     35 .set  _EBP, 8       #size 4
     36 .set  _ESP, 12      #size 4
     37 .set  _EBX, 16      #size 4
     38 .set  _EDX, 20      #size 4
     39 .set  _ECX, 24      #size 4
     40 .set  _EAX, 28      #size 4
     41 .set  _DS,  32      #size 2
     42 .set  _ES,  34      #size 2
     43 .set  _FS,  36      #size 2
     44 .set  _GS,  38      #size 2
     45 .set  _EFLAGS, 40   #size 8
     46 .set  _EIP, 48      #size 4
     47 .set  _CS, 52       #size 2
     48 .set  _SS, 54       #size 2
     49 .set  IA32_REGS_SIZE, 56
     50 
     51     .data
     52 
     53 .set Lm16Size, ASM_PFX(InternalAsmThunk16) - ASM_PFX(m16Start)
     54 ASM_PFX(m16Size):         .word      Lm16Size
     55 .set  LmThunk16Attr, L_ThunkAttr - ASM_PFX(m16Start)
     56 ASM_PFX(mThunk16Attr):    .word      LmThunk16Attr
     57 .set Lm16Gdt, ASM_PFX(NullSeg) - ASM_PFX(m16Start)
     58 ASM_PFX(m16Gdt):          .word      Lm16Gdt
     59 .set Lm16GdtrBase, _16GdtrBase - ASM_PFX(m16Start)
     60 ASM_PFX(m16GdtrBase):     .word      Lm16GdtrBase
     61 .set LmTransition, _EntryPoint - ASM_PFX(m16Start)
     62 ASM_PFX(mTransition):     .word      LmTransition
     63 
     64     .text
     65 
     66 ASM_PFX(m16Start):
     67 
     68 SavedGdt:    .space 10
     69 
     70 #------------------------------------------------------------------------------
     71 # _BackFromUserCode() takes control in real mode after 'retf' has been executed
     72 # by user code. It will be shadowed to somewhere in memory below 1MB.
     73 #------------------------------------------------------------------------------
     74 ASM_GLOBAL ASM_PFX(BackFromUserCode)
     75 ASM_PFX(BackFromUserCode):
     76     #
     77     # The order of saved registers on the stack matches the order they appears
     78     # in IA32_REGS structure. This facilitates wrapper function to extract them
     79     # into that structure.
     80     #
     81     # Some instructions for manipulation of segment registers have to be written
     82     # in opcode since 64-bit MASM prevents accesses to those registers.
     83     #
     84     .byte 0x16                          # push ss
     85     .byte 0xe                           # push cs
     86     .byte 0x66
     87     call    L_Base                       # push eip
     88 L_Base:
     89     .byte 0x66
     90     pushq   $0                          # reserved high order 32 bits of EFlags
     91     .byte 0x66, 0x9c                    # pushfd actually
     92     cli                                 # disable interrupts
     93     push    %gs
     94     push    %fs
     95     .byte 6                             # push es
     96     .byte 0x1e                          # push ds
     97     .byte 0x66,0x60                     # pushad
     98     .byte 0x66,0xba                     # mov edx, imm32
     99 L_ThunkAttr:  .space  4
    100     testb   $THUNK_ATTRIBUTE_DISABLE_A20_MASK_INT_15, %dl
    101     jz      L_1
    102     movl    $0x15cd2401,%eax            # mov ax, 2401h & int 15h
    103     cli                                 # disable interrupts
    104     jnc     L_2
    105 L_1:
    106     testb   $THUNK_ATTRIBUTE_DISABLE_A20_MASK_KBD_CTRL, %dl
    107     jz      L_2
    108     inb     $0x92,%al
    109     orb     $2,%al
    110     outb    %al, $0x92                   # deactivate A20M#
    111 L_2:
    112     xorw    %ax, %ax                     # xor eax, eax
    113     movl    %ss, %eax                    # mov ax, ss
    114     lea     IA32_REGS_SIZE(%esp), %bp
    115     #
    116     # rsi in the following 2 instructions is indeed bp in 16-bit code
    117     #
    118     movw    %bp, (_ESP - IA32_REGS_SIZE)(%rsi)
    119     .byte 0x66
    120     movl    (_EIP - IA32_REGS_SIZE)(%rsi), %ebx
    121     shlw    $4,%ax                      # shl eax, 4
    122     addw    %ax,%bp                     # add ebp, eax
    123     movw    %cs,%ax
    124     shlw    $4,%ax
    125     lea     (L_64BitCode - L_Base)(%ebx, %eax), %ax
    126     .byte 0x66,0x2e,0x89,0x87           # mov cs:[bx + (L_64Eip - L_Base)], eax
    127     .word   L_64Eip - L_Base
    128     .byte 0x66,0xb8                     # mov eax, imm32
    129 L_SavedCr4: .space      4
    130     movq    %rax, %cr4
    131     #
    132     # rdi in the instruction below is indeed bx in 16-bit code
    133     #
    134     .byte 0x66,0x2e                     # 2eh is "cs:" segment override
    135     lgdt    (SavedGdt - L_Base)(%rdi)
    136     .byte 0x66
    137     movl    $0xc0000080,%ecx
    138     rdmsr
    139     orb     $1,%ah
    140     wrmsr
    141     .byte 0x66,0xb8                     # mov eax, imm32
    142 L_SavedCr0: .space      4
    143     movq    %rax, %cr0
    144     .byte 0x66,0xea                     # jmp far cs:L_64Bit
    145 L_64Eip:    .space      4
    146 L_SavedCs:  .space      2
    147 L_64BitCode:
    148     .byte   0x90
    149     .byte   0x48,0xbc                  # mov rsp, imm64
    150 L_SavedSp:  .space      8              # restore stack
    151     nop
    152     ret
    153 
    154 _EntryPoint: .long      ASM_PFX(ToUserCode) - ASM_PFX(m16Start)
    155              .word      CODE16
    156 _16Gdtr:     .word      GDT_SIZE - 1
    157 _16GdtrBase: .quad      0
    158 _16Idtr:     .word      0x3ff
    159              .long      0
    160 
    161 #------------------------------------------------------------------------------
    162 # _ToUserCode() takes control in real mode before passing control to user code.
    163 # It will be shadowed to somewhere in memory below 1MB.
    164 #------------------------------------------------------------------------------
    165 ASM_GLOBAL ASM_PFX(ToUserCode)
    166 ASM_PFX(ToUserCode):
    167     movl    %edx,%ss                    # set new segment selectors
    168     movl    %edx,%ds
    169     movl    %edx,%es
    170     movl    %edx,%fs
    171     movl    %edx,%gs
    172     .byte 0x66
    173     movl    $0xc0000080,%ecx
    174     movq    %rax, %cr0
    175     rdmsr
    176     andb    $0xfe, %ah                  # $0b11111110
    177     wrmsr
    178     movq    %rbp, %cr4
    179     movl    %esi,%ss                    # set up 16-bit stack segment
    180     movw    %bx,%sp                     # set up 16-bit stack pointer
    181     .byte 0x66                          # make the following call 32-bit
    182     call    L_Base1                       # push eip
    183 L_Base1:
    184     popw    %bp                         # ebp <- address of L_Base1
    185     pushq   (IA32_REGS_SIZE + 2)(%esp)
    186     lea     0x0c(%rsi), %eax
    187     pushq   %rax
    188     lret                                # execution begins at next instruction
    189 L_RealMode:
    190     .byte 0x66,0x2e                     # CS and operand size override
    191     lidt    (_16Idtr - L_Base1)(%rsi)
    192     .byte 0x66,0x61                     # popad
    193     .byte 0x1f                          # pop ds
    194     .byte 0x7                           # pop es
    195     .byte 0x0f, 0xa1                    # pop fs
    196     .byte 0x0f, 0xa9                    # pop gs
    197     .byte 0x66, 0x9d                    # popfd
    198     leaw    4(%esp),%sp                 # skip high order 32 bits of EFlags
    199     .byte 0x66                          # make the following retf 32-bit
    200     lret                                # transfer control to user code
    201 
    202 .set  CODE16,  ASM_PFX(_16Code) - .
    203 .set  DATA16,  ASM_PFX(_16Data) - .
    204 .set  DATA32,  ASM_PFX(_32Data) - .
    205 
    206 ASM_PFX(NullSeg):   .quad      0
    207 ASM_PFX(_16Code):
    208             .word -1
    209             .word 0
    210             .byte 0
    211             .byte 0x9b
    212             .byte 0x8f                  # 16-bit segment, 4GB limit
    213             .byte 0
    214 ASM_PFX(_16Data):
    215             .word -1
    216             .word 0
    217             .byte 0
    218             .byte 0x93
    219             .byte 0x8f                  # 16-bit segment, 4GB limit
    220             .byte 0
    221 ASM_PFX(_32Data):
    222             .word -1
    223             .word 0
    224             .byte 0
    225             .byte 0x93
    226             .byte 0xcf                  # 16-bit segment, 4GB limit
    227             .byte 0
    228 
    229 .set  GDT_SIZE, . - ASM_PFX(NullSeg)
    230 
    231 #------------------------------------------------------------------------------
    232 # IA32_REGISTER_SET *
    233 # EFIAPI
    234 # InternalAsmThunk16 (
    235 #   IN      IA32_REGISTER_SET         *RegisterSet,
    236 #   IN OUT  VOID                      *Transition
    237 #   );
    238 #------------------------------------------------------------------------------
    239 
    240 ASM_GLOBAL ASM_PFX(InternalAsmThunk16)
    241 ASM_PFX(InternalAsmThunk16):
    242     pushq   %rbp
    243     pushq   %rbx
    244     pushq   %rsi
    245     pushq   %rdi
    246 
    247     movl    %ds, %ebx
    248     pushq   %rbx      # Save ds segment register on the stack
    249     movl    %es, %ebx
    250     pushq   %rbx      # Save es segment register on the stack
    251     movl    %ss, %ebx
    252     pushq   %rbx      # Save ss segment register on the stack
    253 
    254     .byte   0x0f, 0xa0                  #push   fs
    255     .byte   0x0f, 0xa8                  #push   gs
    256     movq    %rcx, %rsi
    257     movzwl  _SS(%rsi), %r8d
    258     movl    _ESP(%rsi), %edi
    259     lea     -(IA32_REGS_SIZE + 4)(%edi), %rdi
    260     imul    $16, %r8d, %eax
    261     movl    %edi,%ebx                   # ebx <- stack for 16-bit code
    262     pushq   $(IA32_REGS_SIZE / 4)
    263     addl    %eax,%edi                   # edi <- linear address of 16-bit stack
    264     popq    %rcx
    265     rep
    266     movsl                               # copy RegSet
    267     lea     (L_SavedCr4 - ASM_PFX(m16Start))(%rdx), %ecx
    268     movl    %edx,%eax                   # eax <- transition code address
    269     andl    $0xf,%edx
    270     shll    $12,%eax                    # segment address in high order 16 bits
    271     .set LBackFromUserCodeDelta, ASM_PFX(BackFromUserCode) - ASM_PFX(m16Start)
    272     lea     (LBackFromUserCodeDelta)(%rdx), %ax
    273     stosl                               # [edi] <- return address of user code
    274     sgdt    0x60(%rsp)                  # save GDT stack in argument space
    275     movzwq  0x60(%rsp), %r10            # r10 <- GDT limit
    276     lea     ((ASM_PFX(InternalAsmThunk16) - L_SavedCr4) + 0xf)(%rcx), %r11
    277     andq    $0xfffffffffffffff0, %r11   # r11 <- 16-byte aligned shadowed GDT table in real mode buffer
    278 
    279     movw    %r10w, (SavedGdt - L_SavedCr4)(%rcx)       # save the limit of shadowed GDT table
    280     movq    %r11, (SavedGdt - L_SavedCr4 + 0x2)(%rcx)  # save the base address of shadowed GDT table
    281 
    282     movq    0x62(%rsp) ,%rsi            # rsi <- the original GDT base address
    283     xchg   %r10, %rcx                   # save rcx to r10 and initialize rcx to be the limit of GDT table
    284     incq   %rcx                         # rcx <- the size of memory to copy
    285     xchg   %r11, %rdi                   # save rdi to r11 and initialize rdi to the base address of shadowed GDT table
    286     rep
    287     movsb                               # perform memory copy to shadow GDT table
    288     movq   %r10, %rcx                   # restore the orignal rcx before memory copy
    289     movq   %r11, %rdi                   # restore the original rdi before memory copy
    290 
    291     sidt    0x50(%rsp)
    292     movq    %cr0, %rax
    293     .set LSavedCrDelta, L_SavedCr0 - L_SavedCr4
    294     movl    %eax, (LSavedCrDelta)(%rcx)
    295     andl    $0x7ffffffe,%eax            # clear PE, PG bits
    296     movq    %cr4, %rbp
    297     movl    %ebp, (%rcx)                # save CR4 in SavedCr4
    298     andl    $0xffffffcf,%ebp            # clear PAE, PSE bits
    299     movl    %r8d, %esi                  # esi <- 16-bit stack segment
    300     .byte      0x6a, DATA32
    301     popq    %rdx
    302     lgdt    (_16Gdtr - L_SavedCr4)(%rcx)
    303     movl    %edx,%ss
    304     pushfq
    305     lea     -8(%rdx), %edx
    306     lea     L_RetFromRealMode(%rip), %r8
    307     pushq   %r8
    308     movl    %cs, %r8d
    309     movw    %r8w, (L_SavedCs - L_SavedCr4)(%rcx)
    310     movq    %rsp, (L_SavedSp - L_SavedCr4)(%rcx)
    311     .byte   0xff, 0x69                  #  jmp (_EntryPoint - L_SavedCr4)(%rcx)
    312     .set    Ltemp1, _EntryPoint - L_SavedCr4
    313     .byte   Ltemp1
    314 L_RetFromRealMode:
    315     popfq
    316     lgdt    0x60(%rsp)                  # restore protected mode GDTR
    317     lidt    0x50(%rsp)                  # restore protected mode IDTR
    318     lea     -IA32_REGS_SIZE(%rbp), %eax
    319     .byte 0x0f, 0xa9                    # pop gs
    320     .byte 0x0f, 0xa1                    # pop fs
    321 
    322     popq     %rbx
    323     movl     %ebx, %ss
    324     popq     %rbx
    325     movl     %ebx, %es
    326     popq     %rbx
    327     movl     %ebx, %ds
    328 
    329     popq    %rdi
    330     popq    %rsi
    331     popq    %rbx
    332     popq    %rbp
    333 
    334     ret
    335