]> git.proxmox.com Git - mirror_edk2.git/blobdiff - MdePkg/Library/BaseLib/X64/Thunk16.S
Check in implementation in GNU assembly for Thunk16.S in BaseLib.
[mirror_edk2.git] / MdePkg / Library / BaseLib / X64 / Thunk16.S
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..1ff8c800a2256394d04242b2abeba8e0514a69aa 100644 (file)
@@ -0,0 +1,290 @@
+#------------------------------------------------------------------------------
+#
+# Copyright (c) 2006 - 2008, Intel Corporation
+# All rights reserved. This program and the accompanying materials
+# are licensed and made available under the terms and conditions of the BSD License
+# which accompanies this distribution.  The full text of the license may be found at
+# http://opensource.org/licenses/bsd-license.php
+#
+# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+#
+# Module Name:
+#
+#   Thunk16.S
+#
+# Abstract:
+#
+#   Real mode thunk
+#
+#------------------------------------------------------------------------------
+
+#include <Library/BaseLib.h>
+
+# define the structure of IA32_REGS\r
+.equ  _EDI, 0       #size 4\r
+.equ  _ESI, 4       #size 4\r
+.equ  _EBP, 8       #size 4\r
+.equ  _ESP, 12      #size 4\r
+.equ  _EBX, 16      #size 4\r
+.equ  _EDX, 20      #size 4\r
+.equ  _ECX, 24      #size 4\r
+.equ  _EAX, 28      #size 4\r
+.equ  _DS,  32      #size 2\r
+.equ  _ES,  34      #size 2\r
+.equ  _FS,  36      #size 2\r
+.equ  _GS,  38      #size 2\r
+.equ  _EFLAGS, 40   #size 8\r
+.equ  _EIP, 48      #size 4\r
+.equ  _CS, 52       #size 2\r
+.equ  _SS, 54       #size 2\r
+.equ  IA32_REGS_SIZE, 56\r
+
+    .data
+
+m16Size:         .word      _InternalAsmThunk16 - m16Start
+mThunk16Attr:    .word      _ThunkAttr - m16Start
+m16Gdt:          .word      _NullSeg - m16Start
+m16GdtrBase:     .word      _16GdtrBase - m16Start
+mTransition:     .word      _EntryPoint - m16Start
+
+    .text
+
+m16Start:
+
+SavedGdt:    .space 10
+
+#------------------------------------------------------------------------------
+# _BackFromUserCode() takes control in real mode after 'retf' has been executed
+# by user code. It will be shadowed to somewhere in memory below 1MB.
+#------------------------------------------------------------------------------
+.globl ASM_PFX(BackFromUserCode)
+ASM_PFX(BackFromUserCode):
+    #
+    # The order of saved registers on the stack matches the order they appears
+    # in IA32_REGS structure. This facilitates wrapper function to extract them
+    # into that structure.
+    #
+    # Some instructions for manipulation of segment registers have to be written
+    # in opcode since 64-bit MASM prevents accesses to those registers.
+    #
+    .byte 0x16                          # push ss
+    .byte 0xe                           # push cs
+    .byte 0x66
+    call    @Base                       # push eip
+@Base: 
+    .byte 0x66
+    pushq   $0                          # reserved high order 32 bits of EFlags
+    .byte 0x66, 0x9c                    # pushfd actually
+    cli                                 # disable interrupts
+    push    %gs
+    push    %fs
+    .byte 6                             # push es
+    .byte 0x1e                          # push ds
+    .byte 0x66,0x60                     # pushad
+    .byte 0x66,0xba                     # mov edx, imm32
+_ThunkAttr:  .space  4
+    testb   $THUNK_ATTRIBUTE_DISABLE_A20_MASK_INT_15, %dl
+    jz      @1
+    movl    $0x15cd2401,%eax            # mov ax, 2401h & int 15h
+    cli                                 # disable interrupts
+    jnc     @2
+@1: 
+    testb   $THUNK_ATTRIBUTE_DISABLE_A20_MASK_KBD_CTRL, %dl
+    jz      @2
+    inb     $0x92,%al
+    orb     $2,%al
+    outb    %al, $0x92                   # deactivate A20M#
+@2: 
+    movl    %ss,%eax
+    lea     IA32_REGS_SIZE(%esp), %bp
+    #
+    # rsi in the following 2 instructions is indeed bp in 16-bit code
+    #
+    movw    %bp, (_ESP - IA32_REGS_SIZE)(%rsi)
+    .byte 0x66
+    movl    (_EIP - IA32_REGS_SIZE)(%rsi), %ebx
+    shlw    $4,%ax                      # shl eax, 4
+    addw    %ax,%bp                     # add ebp, eax
+    movw    %cs,%ax
+    shlw    $4,%ax
+    lea     (@64BitCode - @Base)(%ebx, %eax), %ax
+    .byte 0x66,0x2e,0x89,0x87           # mov cs:[bx + (@64Eip - @Base)], eax
+    .word   @64Eip - @Base
+    .byte 0x66,0xb8                     # mov eax, imm32
+SavedCr4:   .space  4
+    movq    %rax, %cr4
+    #
+    # rdi in the instruction below is indeed bx in 16-bit code
+    #
+    .byte 0x66,0x2e                     # 2eh is "cs:" segment override
+    lgdt    (SavedGdt - @Base)(%rdi)
+    .byte 0x66
+    movl    $0xc0000080,%ecx
+    rdmsr
+    orb     $1,%ah
+    wrmsr
+    .byte 0x66,0xb8                     # mov eax, imm32
+SavedCr0:    .space      4
+    movq    %rax, %cr0
+    .byte 0x66,0xea                     # jmp far cs:@64Bit
+@64Eip:      .space      4
+SavedCs:     .space      2
+@64BitCode: 
+    movq     %r8, %rsp
+    ret
+
+_EntryPoint: .long      ASM_PFX(ToUserCode) - m16Start
+             .word      CODE16
+_16Gdtr:     .word      GDT_SIZE - 1
+_16GdtrBase: .quad      $_NullSeg
+_16Idtr:     .word      0x3ff
+             .long      0
+
+#------------------------------------------------------------------------------
+# _ToUserCode() takes control in real mode before passing control to user code.
+# It will be shadowed to somewhere in memory below 1MB.
+#------------------------------------------------------------------------------
+.globl ASM_PFX(ToUserCode)
+ASM_PFX(ToUserCode):
+    movl    %edx,%ss                    # set new segment selectors
+    movl    %edx,%ds
+    movl    %edx,%es
+    movl    %edx,%fs
+    movl    %edx,%gs
+    .byte 0x66
+    movl    $0xc0000080,%ecx
+    movq    %rax, %cr0
+    rdmsr
+    andb    $0b11111110, %ah 
+    wrmsr
+    movq    %rbp, %cr4
+    movl    %esi,%ss                    # set up 16-bit stack segment
+    movw    %bx,%sp                     # set up 16-bit stack pointer
+    .byte 0x66                          # make the following call 32-bit
+    call    @Base1                       # push eip
+@Base1: 
+    popw    %bp                         # ebp <- address of @Base1
+    pushq   (IA32_REGS_SIZE + 2)(%esp)
+    lea     0x0c(%rsi), %eax
+    pushq   %rax
+    lret                                # execution begins at next instruction
+@RealMode: 
+    .byte 0x66,0x2e                     # CS and operand size override
+    lidt    (_16Idtr - @Base1)(%rsi)
+    .byte 0x66,0x61                     # popad
+    .byte 0x1f                          # pop ds
+    .byte 0x7                           # pop es
+    .byte 0x0f, 0xa1                    # pop fs
+    .byte 0x0f, 0xa9                    # pop gs
+    .byte 0x66, 0x9d                    # popfd
+    leaw    4(%esp),%sp                 # skip high order 32 bits of EFlags
+    .byte 0x66                          # make the following retf 32-bit
+    lret                                # transfer control to user code
+
+.equ  CODE16,  ASM_PFX(16Code) - .
+.equ  DATA16,  ASM_PFX(16Data) - .
+.equ  DATA32,  ASM_PFX(32Data) - .
+
+_NullSeg:   .quad      0
+ASM_PFX(16Code):
+            .word -1
+            .word 0
+            .byte 0
+            .byte 0x9b
+            .byte 0x8f                  # 16-bit segment, 4GB limit
+            .byte 0
+ASM_PFX(16Data):
+            .word -1
+            .word 0
+            .byte 0
+            .byte 0x93
+            .byte 0x8f                  # 16-bit segment, 4GB limit
+            .byte 0
+ASM_PFX(32Data):
+            .word -1
+            .word 0
+            .byte 0
+            .byte 0x93
+            .byte 0xcf                  # 16-bit segment, 4GB limit
+            .byte 0
+
+.equ  GDT_SIZE, . - ASM_PFX(NullSeg)
+
+#------------------------------------------------------------------------------
+# IA32_REGISTER_SET *
+# EFIAPI
+# InternalAsmThunk16 (
+#   IN      IA32_REGISTER_SET         *RegisterSet,
+#   IN OUT  VOID                      *Transition
+#   );
+#------------------------------------------------------------------------------
+# MISMATCH: "InternalAsmThunk16  PROC    USES    rbp rbx rsi rdi"
+
+.globl ASM_PFX(InternalAsmThunk16)
+ASM_PFX(InternalAsmThunk16):
+    pushq   %rbp
+    pushq   %rbx
+    pushq   %rsi
+    pushq   %rdi
+    
+    movl    %ds, %r10d                  # r9 ~ r11 are not accessible in 16-bit
+    movl    %es, %r11d                  # so use them for saving seg registers
+    movl    %ss, %r9d
+    .byte   0x0f, 0xa0                  #push   fs
+    .byte   0x0f, 0xa8                  #push   gs
+    movq    %rcx, %rsi
+    movzwl  _SS(%rsi), %r8d
+    movl    _ESP(%rsi), %edi
+    lea     -(IA32_REGS_SIZE + 4)(%edi), %rdi
+    imul    $16, %r8d, %eax 
+    movl    %edi,%ebx                   # ebx <- stack for 16-bit code
+    pushq   $(IA32_REGS_SIZE / 4)
+    addl    %eax,%edi                   # edi <- linear address of 16-bit stack
+    popq    %rcx
+    rep
+    movsl                               # copy RegSet
+    lea     (SavedCr4 - m16Start)(%rdx), %ecx
+    movl    %edx,%eax                   # eax <- transition code address
+    andl    $0xf,%edx
+    shll    $12,%eax                    # segment address in high order 16 bits
+    lea     (_BackFromUserCode - m16Start)(%rdx), %ax
+    stosl                               # [edi] <- return address of user code
+    sgdt    (SavedGdt - SavedCr4)(%rcx) 
+    sidt    0x38(%rsp)
+    movq    %cr0, %rax
+    movl    %eax, (SavedCr0 - SavedCr4)(%rcx)
+    andl    $0x7ffffffe,%eax            # clear PE, PG bits
+    movq    %cr4, %rbp
+    movl    %ebp, (%rcx)                # save CR4 in SavedCr4
+    andl    $0x300,%ebp                 # clear all but PCE and OSFXSR bits
+    movl    %r8d, %esi                  # esi <- 16-bit stack segment
+    .byte      0x6a, DATA32
+    popq    %rdx
+    lgdt    (_16Gdtr - SavedCr4)(%rcx)
+    movl    %edx,%ss
+    pushfq
+    lea     -8(%rdx), %edx
+    lea     @RetFromRealMode, %r8
+    pushq   %r8
+    movl    %cs, %r8d
+    movw    %r8w, (SavedCs - SavedCr4)(%rcx)
+    movq     %rsp, %r8
+    .byte   0xff, 0x69                  #  jmp (_EntryPoint - SavedCr4)(%rcx)
+    .byte   _EntryPoint - SavedCr4
+@RetFromRealMode: 
+    popfq
+    lidt    0x38(%rsp)
+    lea     -IA32_REGS_SIZE(%rbp), %eax
+    .byte 0x0f, 0xa9                    # pop gs
+    .byte 0x0f, 0xa1                    # pop fs
+    movl    %r9d, %ss
+    movl    %r11d, %es
+    movl    %r10d, %ds
+    
+    popq    %rdi
+    popq    %rsi
+    popq    %rbx
+    popq    %rbp
+
+    ret