summaryrefslogtreecommitdiffstats
path: root/MdePkg/Library/BaseLib/Ia32/Thunk16.S
blob: 185655eecbe9b676fb0de694b9b72d426b5a7b2c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
#------------------------------------------------------------------------------
#
# Copyright (c) 2006 - 2013, Intel Corporation. All rights reserved.<BR>
# 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>

ASM_GLOBAL ASM_PFX(m16Start), ASM_PFX(m16Size), ASM_PFX(mThunk16Attr), ASM_PFX(m16Gdt), ASM_PFX(m16GdtrBase), ASM_PFX(mTransition)
ASM_GLOBAL ASM_PFX(InternalAsmThunk16)

# define the structure of IA32_REGS
.set  _EDI, 0       #size 4
.set  _ESI, 4       #size 4
.set  _EBP, 8       #size 4
.set  _ESP, 12      #size 4
.set  _EBX, 16      #size 4
.set  _EDX, 20      #size 4
.set  _ECX, 24      #size 4
.set  _EAX, 28      #size 4
.set  _DS,  32      #size 2
.set  _ES,  34      #size 2
.set  _FS,  36      #size 2
.set  _GS,  38      #size 2
.set  _EFLAGS, 40   #size 4
.set  _EIP, 44      #size 4
.set  _CS, 48       #size 2
.set  _SS, 50       #size 2
.set  IA32_REGS_SIZE, 52

    .text
    .code16

ASM_PFX(m16Start):

SavedGdt:     .space  6

ASM_PFX(BackFromUserCode):
    push    %ss
    push    %cs

    calll   L_Base1                     # push eip
L_Base1:
    pushfl
    cli                                 # disable interrupts
    push    %gs
    push    %fs
    push    %es
    push    %ds
    pushal
    .byte   0x66, 0xba                  # mov edx, imm32
ASM_PFX(ThunkAttr): .space  4
    testb   $THUNK_ATTRIBUTE_DISABLE_A20_MASK_INT_15, %dl
    jz      1f
    movw    $0x2401, %ax
    int     $0x15
    cli                                 # disable interrupts
    jnc     2f
1:
    testb   $THUNK_ATTRIBUTE_DISABLE_A20_MASK_KBD_CTRL, %dl
    jz      2f
    inb     $0x92, %al
    orb     $2, %al
    outb    %al, $0x92                  # deactivate A20M#
2:
    xorl    %eax, %eax
    movw    %ss, %ax
    leal    IA32_REGS_SIZE(%esp), %ebp
    mov     %ebp, (_ESP - IA32_REGS_SIZE)(%bp)
    mov     (_EIP - IA32_REGS_SIZE)(%bp), %bx
    shll    $4, %eax
    addl    %eax, %ebp
    .byte   0x66, 0xb8                  # mov eax, imm32
SavedCr4:   .space  4
    movl    %eax, %cr4
    lgdtl   %cs:(SavedGdt - L_Base1)(%bx)
    .byte   0x66, 0xb8                  # mov eax, imm32
SavedCr0:   .space  4
    movl    %eax, %cr0
    .byte   0xb8                        # mov ax, imm16
SavedSs:    .space  2
    movl    %eax, %ss
    .byte   0x66, 0xbc                  # mov esp, imm32
SavedEsp:   .space  4
    lretl                               # return to protected mode

_EntryPoint:    .long      ASM_PFX(ToUserCode) - ASM_PFX(m16Start)
                .word      0x8
_16Idtr:        .word      0x3ff
                .long      0
_16Gdtr:        .word      GdtEnd - _NullSegDesc - 1
_16GdtrBase:    .long      _NullSegDesc

ASM_PFX(ToUserCode):
    movw    %ss, %dx
    movw    %cx, %ss                    # set new segment selectors
    movw    %cx, %ds
    movw    %cx, %es
    movw    %cx, %fs
    movw    %cx, %gs
    movl    %eax, %cr0                  # real mode starts at next instruction
                                        #  which (per SDM) *must* be a far JMP.
    ljmpw   $0,$0                       # will be filled in by InternalAsmThunk16
L_Base:                                 #  to point here.
    movl    %ebp, %cr4
    movw    %si, %ss                    # set up 16-bit stack segment
    xchgl   %ebx, %esp                  # set up 16-bit stack pointer

    movw    IA32_REGS_SIZE(%esp), %bp   # get BackToUserCode address from stack
    mov     %dx, %cs:(SavedSs - ASM_PFX(BackFromUserCode))(%bp)
    mov     %ebx, %cs:(SavedEsp - ASM_PFX(BackFromUserCode))(%bp)
    lidtl   %cs:(_16Idtr - ASM_PFX(BackFromUserCode))(%bp)
    popal
    pop     %ds
    pop     %es
    pop     %fs
    pop     %gs
    popfl
    lretl                               # transfer control to user code

_NullSegDesc:   .quad   0
_16CsDesc:
                .word   -1
                .word   0
                .byte   0
                .byte   0x9b
                .byte   0x8f            # 16-bit segment, 4GB limit
                .byte   0
_16DsDesc:
                .word   -1
                .word   0
                .byte   0
                .byte   0x93
                .byte   0x8f            # 16-bit segment, 4GB limit
                .byte   0
GdtEnd:

    .code32
#
#   @param  RegSet  The pointer to a IA32_DWORD_REGS structure
#   @param  Transition  The pointer to the transition code
#   @return The address of the 16-bit stack after returning from user code
#
ASM_PFX(InternalAsmThunk16):
    push    %ebp
    push    %ebx
    push    %esi
    push    %edi
    push    %ds
    push    %es
    push    %fs
    push    %gs
    movl    36(%esp), %esi              # esi <- RegSet
    movzwl  _SS(%esi), %edx
    mov     _ESP(%esi), %edi
    add     $(-(IA32_REGS_SIZE + 4)), %edi
    movl    %edi, %ebx                  # ebx <- stack offset
    imul    $0x10, %edx, %eax
    push    $(IA32_REGS_SIZE / 4)
    addl    %eax, %edi                  # edi <- linear address of 16-bit stack
    pop     %ecx
    rep
    movsl                               # copy RegSet
    movl    40(%esp), %eax              # eax <- address of transition code
    movl    %edx, %esi                  # esi <- 16-bit stack segment
    lea     (SavedCr0 - ASM_PFX(m16Start))(%eax), %edx
    movl    %eax, %ecx
    andl    $0xf, %ecx
    shll    $12, %eax
    lea     (ASM_PFX(BackFromUserCode) - ASM_PFX(m16Start))(%ecx), %ecx
    movw    %cx, %ax
    stosl                               # [edi] <- return address of user code
    addl    $(L_Base - ASM_PFX(BackFromUserCode)), %eax
    movl    %eax, (L_Base - SavedCr0 - 4)(%edx)
    sgdtl   (SavedGdt - SavedCr0)(%edx)
    sidtl   0x24(%esp)
    movl    %cr0, %eax
    movl    %eax, (%edx)                # save CR0 in SavedCr0
    andl    $0x7ffffffe, %eax           # clear PE, PG bits
    movl    %cr4, %ebp
    mov     %ebp, (SavedCr4 - SavedCr0)(%edx)
    andl    $0xffffffcf, %ebp           # clear PAE, PSE bits
    pushl   $0x10
    pop     %ecx                        # ecx <- selector for data segments
    lgdtl   (_16Gdtr - SavedCr0)(%edx)
    pushfl
    lcall   *(_EntryPoint - SavedCr0)(%edx)
    popfl
    lidtl   0x24(%esp)
    lea     -IA32_REGS_SIZE(%ebp), %eax
    pop     %gs
    pop     %fs
    pop     %es
    pop     %ds
    pop     %edi
    pop     %esi
    pop     %ebx
    pop     %ebp
    ret

    .const:

ASM_PFX(m16Size):        .word      ASM_PFX(InternalAsmThunk16)  - ASM_PFX(m16Start)
ASM_PFX(mThunk16Attr):   .word      ASM_PFX(ThunkAttr)          - ASM_PFX(m16Start)
ASM_PFX(m16Gdt):         .word      _NullSegDesc        - ASM_PFX(m16Start)
ASM_PFX(m16GdtrBase):    .word      _16GdtrBase         - ASM_PFX(m16Start)
ASM_PFX(mTransition):    .word      _EntryPoint         - ASM_PFX(m16Start)