Merge from amd64-branch:
[reactos.git] / reactos / hal / halx86 / generic / v86.s
1 /*
2 * FILE: hal/halx86/generic/bios.S
3 * COPYRIGHT: See COPYING in the top level directory
4 * PURPOSE: V8086 Real-Mode BIOS Thunking
5 * PROGRAMMER: Alex Ionescu (alex@relsoft.net)
6 */
7
8 /* INCLUDES ******************************************************************/
9
10 #include <asm.h>
11 #include <internal/i386/asmmacro.S>
12 .intel_syntax noprefix
13
14 //
15 // HAL BIOS Frame
16 //
17 #define HALP_BIOS_FRAME_SS 0x00
18 #define HALP_BIOS_FRAME_ESP 0x04
19 #define HALP_BIOS_FRAME_EFLAGS 0x08
20 #define HALP_BIOS_FRAME_CS 0x0C
21 #define HALP_BIOS_FRAME_EIP 0x10
22 #define HALP_BIOS_FRAME_TRAP_FRAME 0x14
23 #define HALP_BIOS_FRAME_CS_LIMIT 0x18
24 #define HALP_BIOS_FRAME_CS_BASE 0x1C
25 #define HALP_BIOS_FRAME_CS_FLAGS 0x20
26 #define HALP_BIOS_FRAME_SS_LIMIT 0x24
27 #define HALP_BIOS_FRAME_SS_BASE 0x28
28 #define HALP_BIOS_FRAME_SS_FLAGS 0x2C
29 #define HALP_BIOS_FRAME_PREFIX 0x30
30 #define HALP_BIOS_FRAME_LENGTH 0x34
31
32 /* GLOBALS *******************************************************************/
33
34 _HalpSavedEsp:
35 .long 0
36
37 _InvalidMsg:
38 .asciz "HAL: An invalid V86 opcode was encountered at address %x:%x\n"
39
40 _InvalidGpfMsg:
41 .asciz "HAL: Trap0D while not in V86 mode\n"
42
43 _UnhandledMsg:
44 .asciz "\n\x7\x7!!! Unhandled or Unexpected Code at line: %lx [%s]!!!\n"
45
46 /* FUNCTIONS *****************************************************************/
47
48 .globl _HalpBiosCall@0
49 .func HalpBiosCall@0
50 _HalpBiosCall@0:
51
52 /* Set up stack pointer */
53 push ebp
54 mov ebp, esp
55
56 /* Build a trap frame */
57 pushfd
58 push edi
59 push esi
60 push ebx
61 push ds
62 push es
63 push fs
64 push gs
65 push offset _HalpRealModeEnd
66
67 /* Save the stack */
68 mov _HalpSavedEsp, esp
69
70 /* Turn off alignment faults */
71 mov eax, cr0
72 and eax, ~CR0_AM
73 mov cr0, eax
74
75 /* Setup a new stack */
76 mov esi, fs:KPCR_TSS
77 mov eax, esp
78 sub eax, NPX_FRAME_LENGTH
79 mov [esi+KTSS_ESP0], eax
80
81 /* Save V86 registers */
82 push 0
83 push 0
84 push 0
85 push 0
86 push 0x2000
87
88 /* Get linear delta between stack and code */
89 mov eax, offset _HalpRealModeEnd-4
90 sub eax, offset _HalpRealModeStart
91
92 /* Get offset of code */
93 mov edx, offset _HalpRealModeStart
94 and edx, 0xFFF
95
96 /* Add offset to linear address and save the new V86 SP */
97 add eax, edx
98 push eax
99
100 /* Start building interrupt frame. Setup V86 EFLAGS and IOPL 3 */
101 pushfd
102 or dword ptr [esp], EFLAGS_V86_MASK
103 or dword ptr [esp], 0x3000
104
105 /* Push the CS and IP */
106 push 0x2000
107 push edx
108
109 /* Do the interrupt return (jump to V86 mode) */
110 iretd
111
112 .globl _HalpRealModeStart
113 _HalpRealModeStart:
114
115 /* Set mode 13 */
116 mov ax, 0x12
117 .byte 0
118 .byte 0
119
120 /* Do the interrupt */
121 int 0x10
122
123 /* BOP to exit V86 mode */
124 .byte 0xC4
125 .byte 0xC4
126
127 /* The stack lives here */
128 .align 4
129 .space 2048
130 .globl _HalpRealModeEnd
131 _HalpRealModeEnd:
132
133 /* We're back, clean up the trap frame */
134 pop gs
135 pop fs
136 pop es
137 pop ds
138 pop ebx
139 pop esi
140 pop edi
141 popfd
142
143 /* Return to caller */
144 pop ebp
145 ret 0
146 .endfunc
147
148 .globl _HalpOpcodeInvalid@0
149 .func HalpOpcodeInvalid@0
150 _HalpOpcodeInvalid@0:
151
152 /* This should never happen -- is the IOPM damaged? */
153 push [esi+HALP_BIOS_FRAME_EIP]
154 push [esi+HALP_BIOS_FRAME_CS]
155 push offset _InvalidMsg
156 call _DbgPrint
157 add esp, 12
158
159 /* Break */
160 int 3
161
162 /* Nothing to return */
163 xor eax, eax
164 ret 0
165 .endfunc
166
167 .globl _HalpPushInt@0
168 .func HalpPushInt@0
169 _HalpPushInt@0:
170
171 /* Save EBX */
172 push ebx
173
174 /* Get SS offset and base */
175 mov edx, [esi+HALP_BIOS_FRAME_ESP]
176 mov ebx, [esi+HALP_BIOS_FRAME_SS_BASE]
177
178 /* Convert to 16-bits */
179 and edx, 0xFFFF
180 sub dx, 2
181
182 /* Get EFLAGS and write them into the linear address of SP */
183 mov ax, word ptr [esi+HALP_BIOS_FRAME_EFLAGS]
184 mov [ebx+edx], ax
185 sub dx, 2
186
187 /* Get CS segment and write it into SP */
188 mov ax, word ptr [esi+HALP_BIOS_FRAME_CS]
189 mov [ebx+edx], ax
190 sub dx, 2
191
192 /* Get IP and write it into SP */
193 mov ax, word ptr [esi+HALP_BIOS_FRAME_EIP]
194 mov [ebx+edx], ax
195
196 /* Get new IP value (the interrupt ID is in ECX, so this is in the IVT) */
197 mov eax, [ecx*4]
198 push eax
199
200 /* Now save the new IP */
201 movzx eax, ax
202 mov [esi+HALP_BIOS_FRAME_EIP], eax
203
204 /* Save the new CS of this IP */
205 pop eax
206 shr eax, 16
207 mov [esi+HALP_BIOS_FRAME_CS], eax
208
209 /* Update the stack pointer after our manual interrupt frame construction */
210 mov word ptr [esi+HALP_BIOS_FRAME_ESP], dx
211
212 /* Get CS and convert it to linear format */
213 mov eax, [esi+HALP_BIOS_FRAME_CS]
214 shl eax, 4
215 mov [esi+HALP_BIOS_FRAME_CS_BASE], eax
216 mov dword ptr [esi+HALP_BIOS_FRAME_CS_LIMIT], 0xFFFF
217 mov dword ptr [esi+HALP_BIOS_FRAME_CS_FLAGS], 0
218
219 /* Return success and restore EBX */
220 mov eax, 1
221 pop ebx
222 ret 0
223 .endfunc
224
225 .globl _HalpOpcodeINTnn@0
226 .func HalpOpcodeINTnn@0
227 _HalpOpcodeINTnn@0:
228
229 /* Save non-volatiles and stack */
230 push ebp
231 push esi
232 push ebx
233
234 /* Get SS and convert it to linear format */
235 mov eax, [esi+HALP_BIOS_FRAME_SS]
236 shl eax, 4
237 mov [esi+HALP_BIOS_FRAME_SS_BASE], eax
238 mov dword ptr [esi+HALP_BIOS_FRAME_SS_LIMIT], 0xFFFF
239 mov dword ptr [esi+HALP_BIOS_FRAME_SS_FLAGS], 0
240
241 /* Increase IP and check if we're past the CS limit */
242 inc dword ptr [esi+HALP_BIOS_FRAME_EIP]
243 mov edi, [esi+HALP_BIOS_FRAME_EIP]
244 cmp edi, [esi+HALP_BIOS_FRAME_CS_LIMIT]
245 ja EipLimitReached
246
247 /* Convert IP to linear address and read the interrupt number */
248 add edi, [esi+HALP_BIOS_FRAME_CS_BASE]
249 movzx ecx, byte ptr [edi]
250
251 /* Increase EIP and do the interrupt, check for status */
252 inc dword ptr [esi+HALP_BIOS_FRAME_EIP]
253 call _HalpPushInt@0
254 test eax, 0xFFFF
255 jz Done
256
257 /* Update the trap frame */
258 mov ebp, [esi+HALP_BIOS_FRAME_TRAP_FRAME]
259 mov eax, [esi+HALP_BIOS_FRAME_SS]
260 mov [ebp+KTRAP_FRAME_SS], eax
261 mov eax, [esi+HALP_BIOS_FRAME_ESP]
262 mov [ebp+KTRAP_FRAME_ESP], eax
263 mov eax, [esi+HALP_BIOS_FRAME_CS]
264 mov [ebp+KTRAP_FRAME_CS], eax
265 mov eax, [esi+HALP_BIOS_FRAME_EFLAGS]
266 mov [ebp+KTRAP_FRAME_EFLAGS], eax
267
268 /* Set success code */
269 mov eax, 1
270
271 Done:
272 /* Restore volatiles */
273 pop ebx
274 pop edi
275 pop ebp
276 ret 0
277
278 EipLimitReached:
279 /* Set failure code */
280 xor eax, eax
281 jmp Done
282 .endfunc
283
284 .globl _HalpDispatchV86Opcode@0
285 .func HalpDispatchV86Opcode@0
286 _HalpDispatchV86Opcode@0:
287
288 /* Make space for the HAL BIOS Frame on the stack */
289 push ebp
290 mov ebp, esp
291 sub esp, HALP_BIOS_FRAME_LENGTH
292
293 /* Save non-volatiles */
294 push esi
295 push edi
296
297 /* Save pointer to the trap frame */
298 mov esi, [ebp]
299 mov [ebp-HALP_BIOS_FRAME_LENGTH+HALP_BIOS_FRAME_TRAP_FRAME], esi
300
301 /* Save SS */
302 movzx eax, word ptr [esi+KTRAP_FRAME_SS]
303 mov [ebp-HALP_BIOS_FRAME_LENGTH+HALP_BIOS_FRAME_SS], eax
304
305 /* Save ESP */
306 mov eax, [esi+KTRAP_FRAME_ESP]
307 mov [ebp-HALP_BIOS_FRAME_LENGTH+HALP_BIOS_FRAME_ESP], eax
308
309 /* Save EFLAGS */
310 mov eax, [esi+KTRAP_FRAME_EFLAGS]
311 mov [ebp-HALP_BIOS_FRAME_LENGTH+HALP_BIOS_FRAME_EFLAGS], eax
312
313 /* Save CS */
314 movzx eax, word ptr [esi+KTRAP_FRAME_CS]
315 mov [ebp-HALP_BIOS_FRAME_LENGTH+HALP_BIOS_FRAME_CS], eax
316
317 /* Save EIP */
318 mov eax, [esi+KTRAP_FRAME_EIP]
319 mov [ebp-HALP_BIOS_FRAME_LENGTH+HALP_BIOS_FRAME_EIP], eax
320
321 /* No prefix */
322 xor eax, eax
323 mov [ebp-HALP_BIOS_FRAME_LENGTH+HALP_BIOS_FRAME_PREFIX], eax
324
325 /* Set pointer to HAL BIOS Frame */
326 lea esi, [ebp-HALP_BIOS_FRAME_LENGTH]
327
328 /* Convert CS to linear format */
329 mov eax, [esi+HALP_BIOS_FRAME_CS]
330 shl eax, 4
331 mov [esi+HALP_BIOS_FRAME_CS_BASE], eax
332 mov dword ptr [esi+HALP_BIOS_FRAME_CS_LIMIT], 0xFFFF
333 mov dword ptr [esi+HALP_BIOS_FRAME_CS_FLAGS], 0
334
335 /* Make sure IP is within the CS Limit */
336 mov edi, [esi+HALP_BIOS_FRAME_EIP]
337 cmp edi, [esi+HALP_BIOS_FRAME_CS_LIMIT]
338 ja DispatchError
339
340 /* Convert IP to linear address and read the opcode */
341 add edi, [esi+HALP_BIOS_FRAME_CS_BASE]
342 mov dl, [edi]
343
344 /* We only deal with interrupts */
345 cmp dl, 0xCD
346 je DispatchInt
347
348 /* Anything else is invalid */
349 call _HalpOpcodeInvalid@0
350 jmp DispatchError
351
352 DispatchInt:
353 /* Handle dispatching the interrupt */
354 call _HalpOpcodeINTnn@0
355 test eax, 0xFFFF
356 jz DispatchReturn
357
358 /* Update the trap frame EIP */
359 mov edi, [ebp-0x20]
360 mov eax, [ebp-0x24]
361 mov [edi+KTRAP_FRAME_EIP], eax
362
363 /* Set success code */
364 mov eax, 1
365
366 DispatchReturn:
367 /* Restore registers and return */
368 pop edi
369 pop esi
370 mov esp, ebp
371 pop ebp
372 ret 0
373
374 DispatchError:
375 /* Set failure code and return */
376 xor eax, eax
377 jmp DispatchReturn
378 .endfunc
379
380 .func Ki16BitStackException
381 _Ki16BitStackException:
382
383 /* Save stack */
384 push ss
385 push esp
386
387 /* Go to kernel mode thread stack */
388 mov eax, PCR[KPCR_CURRENT_THREAD]
389 add esp, [eax+KTHREAD_INITIAL_STACK]
390
391 /* Switch to good stack segment */
392 UNHANDLED_PATH "16-Bit Stack"
393 .endfunc
394
395 .globl _HalpTrap0D@0
396 .func HalpTrap0D@0
397 TRAP_FIXUPS htd_a, htd_t, DoFixupV86, DoFixupAbios
398 _HalpTrap0D@0:
399
400 /* Enter trap */
401 TRAP_PROLOG htd_a, htd_t
402
403 /* Check if this is a V86 trap */
404 test dword ptr [ebp+KTRAP_FRAME_EFLAGS], EFLAGS_V86_MASK
405 jnz DoDispatch
406
407 /* This is weird -- but might happen during an NMI */
408 push offset _InvalidGpfMsg
409 call _DbgPrint
410 add esp, 4
411
412 /* Loop forever */
413 jmp $
414
415 DoDispatch:
416 /* Handle the opcode */
417 call _HalpDispatchV86Opcode@0
418
419 /* Exit the interrupt */
420 jmp _Kei386EoiHelper@0
421 .endfunc
422
423 .globl _HalpTrap06@0
424 .func HalpTrap06@0
425 _HalpTrap06@0:
426
427 /* Restore DS/ES segments */
428 mov eax, KGDT_R3_DATA | RPL_MASK
429 mov ds, ax
430 mov es, ax
431
432 /* Restore ESP and return */
433 mov esp, _HalpSavedEsp
434 ret 0
435 .endfunc