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
24ASM_GLOBAL ASM_PFX(m16Start), ASM_PFX(m16Size), ASM_PFX(mThunk16Attr), ASM_PFX(m16Gdt), ASM_PFX(m16GdtrBase), ASM_PFX(mTransition)
25ASM_GLOBAL ASM_PFX(InternalAsmThunk16)
26
27# define the structure of IA32_REGS
28.set  _EDI, 0       #size 4
29.set  _ESI, 4       #size 4
30.set  _EBP, 8       #size 4
31.set  _ESP, 12      #size 4
32.set  _EBX, 16      #size 4
33.set  _EDX, 20      #size 4
34.set  _ECX, 24      #size 4
35.set  _EAX, 28      #size 4
36.set  _DS,  32      #size 2
37.set  _ES,  34      #size 2
38.set  _FS,  36      #size 2
39.set  _GS,  38      #size 2
40.set  _EFLAGS, 40   #size 4
41.set  _EIP, 44      #size 4
42.set  _CS, 48       #size 2
43.set  _SS, 50       #size 2
44.set  IA32_REGS_SIZE, 52
45
46    .text
47    .code16
48
49ASM_PFX(m16Start):
50
51SavedGdt:     .space  6
52
53ASM_PFX(BackFromUserCode):
54    push    %ss
55    push    %cs
56
57    calll   L_Base1                     # push eip
58L_Base1:
59    pushfl
60    cli                                 # disable interrupts
61    push    %gs
62    push    %fs
63    push    %es
64    push    %ds
65    pushal
66    .byte   0x66, 0xba                  # mov edx, imm32
67ASM_PFX(ThunkAttr): .space  4
68    testb   $THUNK_ATTRIBUTE_DISABLE_A20_MASK_INT_15, %dl
69    jz      1f
70    movw    $0x2401, %ax
71    int     $0x15
72    cli                                 # disable interrupts
73    jnc     2f
741:
75    testb   $THUNK_ATTRIBUTE_DISABLE_A20_MASK_KBD_CTRL, %dl
76    jz      2f
77    inb     $0x92, %al
78    orb     $2, %al
79    outb    %al, $0x92                  # deactivate A20M#
802:
81    xorl    %eax, %eax
82    movw    %ss, %ax
83    leal    IA32_REGS_SIZE(%esp), %ebp
84    mov     %ebp, (_ESP - IA32_REGS_SIZE)(%bp)
85    mov     (_EIP - IA32_REGS_SIZE)(%bp), %bx
86    shll    $4, %eax
87    addl    %eax, %ebp
88    .byte   0x66, 0xb8                  # mov eax, imm32
89SavedCr4:   .space  4
90    movl    %eax, %cr4
91    lgdtl   %cs:(SavedGdt - L_Base1)(%bx)
92    .byte   0x66, 0xb8                  # mov eax, imm32
93SavedCr0:   .space  4
94    movl    %eax, %cr0
95    .byte   0xb8                        # mov ax, imm16
96SavedSs:    .space  2
97    movl    %eax, %ss
98    .byte   0x66, 0xbc                  # mov esp, imm32
99SavedEsp:   .space  4
100    lretl                               # return to protected mode
101
102_EntryPoint:    .long      ASM_PFX(ToUserCode) - ASM_PFX(m16Start)
103                .word      0x8
104_16Idtr:        .word      0x3ff
105                .long      0
106_16Gdtr:        .word      GdtEnd - _NullSegDesc - 1
107_16GdtrBase:    .long      _NullSegDesc
108
109ASM_PFX(ToUserCode):
110    movw    %ss, %dx
111    movw    %cx, %ss                    # set new segment selectors
112    movw    %cx, %ds
113    movw    %cx, %es
114    movw    %cx, %fs
115    movw    %cx, %gs
116    movl    %eax, %cr0                  # real mode starts at next instruction
117                                        #  which (per SDM) *must* be a far JMP.
118    ljmpw   $0,$0                       # will be filled in by InternalAsmThunk16
119L_Base:                                 #  to point here.
120    movl    %ebp, %cr4
121    movw    %si, %ss                    # set up 16-bit stack segment
122    xchgl   %ebx, %esp                  # set up 16-bit stack pointer
123
124    movw    IA32_REGS_SIZE(%esp), %bp   # get BackToUserCode address from stack
125    mov     %dx, %cs:(SavedSs - ASM_PFX(BackFromUserCode))(%bp)
126    mov     %ebx, %cs:(SavedEsp - ASM_PFX(BackFromUserCode))(%bp)
127    lidtl   %cs:(_16Idtr - ASM_PFX(BackFromUserCode))(%bp)
128    popal
129    pop     %ds
130    pop     %es
131    pop     %fs
132    pop     %gs
133    popfl
134    lretl                               # transfer control to user code
135
136_NullSegDesc:   .quad   0
137_16CsDesc:
138                .word   -1
139                .word   0
140                .byte   0
141                .byte   0x9b
142                .byte   0x8f            # 16-bit segment, 4GB limit
143                .byte   0
144_16DsDesc:
145                .word   -1
146                .word   0
147                .byte   0
148                .byte   0x93
149                .byte   0x8f            # 16-bit segment, 4GB limit
150                .byte   0
151GdtEnd:
152
153    .code32
154#
155#   @param  RegSet  The pointer to a IA32_DWORD_REGS structure
156#   @param  Transition  The pointer to the transition code
157#   @return The address of the 16-bit stack after returning from user code
158#
159ASM_PFX(InternalAsmThunk16):
160    push    %ebp
161    push    %ebx
162    push    %esi
163    push    %edi
164    push    %ds
165    push    %es
166    push    %fs
167    push    %gs
168    movl    36(%esp), %esi              # esi <- RegSet
169    movzwl  _SS(%esi), %edx
170    mov     _ESP(%esi), %edi
171    add     $(-(IA32_REGS_SIZE + 4)), %edi
172    movl    %edi, %ebx                  # ebx <- stack offset
173    imul    $0x10, %edx, %eax
174    push    $(IA32_REGS_SIZE / 4)
175    addl    %eax, %edi                  # edi <- linear address of 16-bit stack
176    pop     %ecx
177    rep
178    movsl                               # copy RegSet
179    movl    40(%esp), %eax              # eax <- address of transition code
180    movl    %edx, %esi                  # esi <- 16-bit stack segment
181    lea     (SavedCr0 - ASM_PFX(m16Start))(%eax), %edx
182    movl    %eax, %ecx
183    andl    $0xf, %ecx
184    shll    $12, %eax
185    lea     (ASM_PFX(BackFromUserCode) - ASM_PFX(m16Start))(%ecx), %ecx
186    movw    %cx, %ax
187    stosl                               # [edi] <- return address of user code
188    addl    $(L_Base - ASM_PFX(BackFromUserCode)), %eax
189    movl    %eax, (L_Base - SavedCr0 - 4)(%edx)
190    sgdtl   (SavedGdt - SavedCr0)(%edx)
191    sidtl   0x24(%esp)
192    movl    %cr0, %eax
193    movl    %eax, (%edx)                # save CR0 in SavedCr0
194    andl    $0x7ffffffe, %eax           # clear PE, PG bits
195    movl    %cr4, %ebp
196    mov     %ebp, (SavedCr4 - SavedCr0)(%edx)
197    andl    $0xffffffcf, %ebp           # clear PAE, PSE bits
198    pushl   $0x10
199    pop     %ecx                        # ecx <- selector for data segments
200    lgdtl   (_16Gdtr - SavedCr0)(%edx)
201    pushfl
202    lcall   *(_EntryPoint - SavedCr0)(%edx)
203    popfl
204    lidtl   0x24(%esp)
205    lea     -IA32_REGS_SIZE(%ebp), %eax
206    pop     %gs
207    pop     %fs
208    pop     %es
209    pop     %ds
210    pop     %edi
211    pop     %esi
212    pop     %ebx
213    pop     %ebp
214    ret
215
216    .const:
217
218ASM_PFX(m16Size):        .word      ASM_PFX(InternalAsmThunk16)  - ASM_PFX(m16Start)
219ASM_PFX(mThunk16Attr):   .word      ASM_PFX(ThunkAttr)          - ASM_PFX(m16Start)
220ASM_PFX(m16Gdt):         .word      _NullSegDesc        - ASM_PFX(m16Start)
221ASM_PFX(m16GdtrBase):    .word      _16GdtrBase         - ASM_PFX(m16Start)
222ASM_PFX(mTransition):    .word      _EntryPoint         - ASM_PFX(m16Start)
223