[FAST486]
[reactos.git] / reactos / lib / fast486 / common.c
1 /*
2 * Fast486 386/486 CPU Emulation Library
3 * common.c
4 *
5 * Copyright (C) 2015 Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 */
21
22 /* INCLUDES *******************************************************************/
23
24 #include <windef.h>
25
26 // #define NDEBUG
27 #include <debug.h>
28
29 #include <fast486.h>
30 #include "common.h"
31
32 /* PUBLIC FUNCTIONS ***********************************************************/
33
34 BOOLEAN
35 Fast486ReadMemory(PFAST486_STATE State,
36 FAST486_SEG_REGS SegmentReg,
37 ULONG Offset,
38 BOOLEAN InstFetch,
39 PVOID Buffer,
40 ULONG Size)
41 {
42 ULONG LinearAddress;
43 PFAST486_SEG_REG CachedDescriptor;
44
45 ASSERT(SegmentReg < FAST486_NUM_SEG_REGS);
46
47 /* Get the cached descriptor */
48 CachedDescriptor = &State->SegmentRegs[SegmentReg];
49
50 if (InstFetch || !CachedDescriptor->DirConf)
51 {
52 if ((Offset + Size - 1) > CachedDescriptor->Limit)
53 {
54 /* Read beyond limit */
55 Fast486Exception(State, FAST486_EXCEPTION_GP);
56 return FALSE;
57 }
58 }
59 else
60 {
61 if (Offset < CachedDescriptor->Limit)
62 {
63 /* Read beyond limit */
64 Fast486Exception(State, FAST486_EXCEPTION_GP);
65 return FALSE;
66 }
67 }
68
69 /* Check for protected mode */
70 if (State->ControlRegisters[FAST486_REG_CR0] & FAST486_CR0_PE)
71 {
72 /* Privilege checks */
73
74 if (!CachedDescriptor->Present)
75 {
76 Fast486Exception(State, FAST486_EXCEPTION_NP);
77 return FALSE;
78 }
79
80 if ((!InstFetch && (CachedDescriptor->Rpl > CachedDescriptor->Dpl))
81 || (Fast486GetCurrentPrivLevel(State) > CachedDescriptor->Dpl))
82 {
83 Fast486Exception(State, FAST486_EXCEPTION_GP);
84 return FALSE;
85 }
86
87 if (InstFetch)
88 {
89 if (!CachedDescriptor->Executable)
90 {
91 /* Data segment not executable */
92 Fast486Exception(State, FAST486_EXCEPTION_GP);
93 return FALSE;
94 }
95 }
96 else
97 {
98 if (CachedDescriptor->Executable && (!CachedDescriptor->ReadWrite))
99 {
100 /* Code segment not readable */
101 Fast486Exception(State, FAST486_EXCEPTION_GP);
102 return FALSE;
103 }
104 }
105 }
106
107 /* Find the linear address */
108 LinearAddress = CachedDescriptor->Base + Offset;
109
110 #ifndef FAST486_NO_PREFETCH
111 if (InstFetch && ((Offset + FAST486_CACHE_SIZE - 1) <= CachedDescriptor->Limit))
112 {
113 State->PrefetchAddress = LinearAddress;
114
115 if ((State->ControlRegisters[FAST486_REG_CR0] & FAST486_CR0_PG)
116 && (PAGE_OFFSET(State->PrefetchAddress) > (FAST486_PAGE_SIZE - FAST486_CACHE_SIZE)))
117 {
118 /* We mustn't prefetch across a page boundary */
119 State->PrefetchAddress = PAGE_ALIGN(State->PrefetchAddress)
120 | (FAST486_PAGE_SIZE - FAST486_CACHE_SIZE);
121
122 if ((LinearAddress - State->PrefetchAddress + Size) >= FAST486_CACHE_SIZE)
123 {
124 /* We can't prefetch without possibly violating page permissions */
125 State->PrefetchValid = FALSE;
126 return Fast486ReadLinearMemory(State, LinearAddress, Buffer, Size);
127 }
128 }
129
130 /* Prefetch */
131 if (Fast486ReadLinearMemory(State,
132 State->PrefetchAddress,
133 State->PrefetchCache,
134 FAST486_CACHE_SIZE))
135 {
136 State->PrefetchValid = TRUE;
137
138 RtlMoveMemory(Buffer,
139 &State->PrefetchCache[LinearAddress - State->PrefetchAddress],
140 Size);
141 return TRUE;
142 }
143 else
144 {
145 State->PrefetchValid = FALSE;
146 return FALSE;
147 }
148 }
149 else
150 #endif
151 {
152 /* Read from the linear address */
153 return Fast486ReadLinearMemory(State, LinearAddress, Buffer, Size);
154 }
155 }
156
157 BOOLEAN
158 Fast486WriteMemory(PFAST486_STATE State,
159 FAST486_SEG_REGS SegmentReg,
160 ULONG Offset,
161 PVOID Buffer,
162 ULONG Size)
163 {
164 ULONG LinearAddress;
165 PFAST486_SEG_REG CachedDescriptor;
166
167 ASSERT(SegmentReg < FAST486_NUM_SEG_REGS);
168
169 /* Get the cached descriptor */
170 CachedDescriptor = &State->SegmentRegs[SegmentReg];
171
172 if (!CachedDescriptor->DirConf)
173 {
174 if ((Offset + Size - 1) > CachedDescriptor->Limit)
175 {
176 /* Write beyond limit */
177 Fast486Exception(State, FAST486_EXCEPTION_GP);
178 return FALSE;
179 }
180 }
181 else
182 {
183 if (Offset < CachedDescriptor->Limit)
184 {
185 /* Read beyond limit */
186 Fast486Exception(State, FAST486_EXCEPTION_GP);
187 return FALSE;
188 }
189 }
190
191 /* Check for protected mode */
192 if (State->ControlRegisters[FAST486_REG_CR0] & FAST486_CR0_PE)
193 {
194 /* Privilege checks */
195
196 if (!CachedDescriptor->Present)
197 {
198 Fast486Exception(State, FAST486_EXCEPTION_NP);
199 return FALSE;
200 }
201
202 if ((CachedDescriptor->Rpl > CachedDescriptor->Dpl)
203 || (Fast486GetCurrentPrivLevel(State) > CachedDescriptor->Dpl))
204 {
205 Fast486Exception(State, FAST486_EXCEPTION_GP);
206 return FALSE;
207 }
208
209 if (CachedDescriptor->Executable)
210 {
211 /* Code segment not writable */
212 Fast486Exception(State, FAST486_EXCEPTION_GP);
213 return FALSE;
214 }
215 else if (!CachedDescriptor->ReadWrite)
216 {
217 /* Data segment not writeable */
218 Fast486Exception(State, FAST486_EXCEPTION_GP);
219 return FALSE;
220 }
221 }
222
223 /* Find the linear address */
224 LinearAddress = CachedDescriptor->Base + Offset;
225
226 #ifndef FAST486_NO_PREFETCH
227 if (State->PrefetchValid
228 && (LinearAddress >= State->PrefetchAddress)
229 && ((LinearAddress + Size) <= (State->PrefetchAddress + FAST486_CACHE_SIZE)))
230 {
231 /* Update the prefetch */
232 RtlMoveMemory(&State->PrefetchCache[LinearAddress - State->PrefetchAddress],
233 Buffer,
234 min(Size, FAST486_CACHE_SIZE + State->PrefetchAddress - LinearAddress));
235 }
236 #endif
237
238 /* Write to the linear address */
239 return Fast486WriteLinearMemory(State, LinearAddress, Buffer, Size);
240 }
241
242 static inline BOOLEAN
243 FASTCALL
244 Fast486GetIntVector(PFAST486_STATE State,
245 UCHAR Number,
246 PFAST486_IDT_ENTRY IdtEntry)
247 {
248 /* Check for protected mode */
249 if (State->ControlRegisters[FAST486_REG_CR0] & FAST486_CR0_PE)
250 {
251 /* Read from the IDT */
252 if (!Fast486ReadLinearMemory(State,
253 State->Idtr.Address
254 + Number * sizeof(*IdtEntry),
255 IdtEntry,
256 sizeof(*IdtEntry)))
257 {
258 /* Exception occurred */
259 return FALSE;
260 }
261 }
262 else
263 {
264 /* Read from the real-mode IVT */
265 ULONG FarPointer;
266
267 /* Paging is always disabled in real mode */
268 State->MemReadCallback(State,
269 State->Idtr.Address
270 + Number * sizeof(FarPointer),
271 &FarPointer,
272 sizeof(FarPointer));
273
274 /* Fill a fake IDT entry */
275 IdtEntry->Offset = LOWORD(FarPointer);
276 IdtEntry->Selector = HIWORD(FarPointer);
277 IdtEntry->Zero = 0;
278 IdtEntry->Type = FAST486_IDT_INT_GATE;
279 IdtEntry->Storage = FALSE;
280 IdtEntry->Dpl = 0;
281 IdtEntry->Present = TRUE;
282 IdtEntry->OffsetHigh = 0;
283 }
284
285 return TRUE;
286 }
287
288 static inline BOOLEAN
289 FASTCALL
290 Fast486InterruptInternal(PFAST486_STATE State,
291 PFAST486_IDT_ENTRY IdtEntry,
292 BOOLEAN PushErrorCode,
293 ULONG ErrorCode)
294 {
295 BOOLEAN GateSize = (IdtEntry->Type == FAST486_IDT_INT_GATE_32) ||
296 (IdtEntry->Type == FAST486_IDT_TRAP_GATE_32);
297 USHORT OldCs = State->SegmentRegs[FAST486_REG_CS].Selector;
298 ULONG OldEip = State->InstPtr.Long;
299 ULONG OldFlags = State->Flags.Long;
300 UCHAR OldCpl = State->Cpl;
301
302 /* Check for protected mode */
303 if (State->ControlRegisters[FAST486_REG_CR0] & FAST486_CR0_PE)
304 {
305 USHORT OldSs = State->SegmentRegs[FAST486_REG_SS].Selector;
306 ULONG OldEsp = State->GeneralRegs[FAST486_REG_ESP].Long;
307
308 if (IdtEntry->Type == FAST486_TASK_GATE_SIGNATURE)
309 {
310 /* Task call */
311 return Fast486TaskSwitch(State, FAST486_TASK_CALL, IdtEntry->Selector);
312 }
313
314 /* Check if the interrupt handler is more privileged or if we're in V86 mode */
315 if ((OldCpl > GET_SEGMENT_RPL(IdtEntry->Selector)) || State->Flags.Vm)
316 {
317 FAST486_TSS Tss;
318
319 /* Read the TSS */
320 if (!Fast486ReadLinearMemory(State,
321 State->TaskReg.Base,
322 &Tss,
323 sizeof(Tss)))
324 {
325 /* Exception occurred */
326 return FALSE;
327 }
328
329 /* Switch to the new privilege level */
330 State->Cpl = GET_SEGMENT_RPL(IdtEntry->Selector);
331
332 /* Check the new (higher) privilege level */
333 switch (State->Cpl)
334 {
335 case 0:
336 {
337 if (!Fast486LoadSegment(State, FAST486_REG_SS, Tss.Ss0))
338 {
339 /* Exception occurred */
340 return FALSE;
341 }
342 State->GeneralRegs[FAST486_REG_ESP].Long = Tss.Esp0;
343
344 break;
345 }
346
347 case 1:
348 {
349 if (!Fast486LoadSegment(State, FAST486_REG_SS, Tss.Ss1))
350 {
351 /* Exception occurred */
352 return FALSE;
353 }
354 State->GeneralRegs[FAST486_REG_ESP].Long = Tss.Esp1;
355
356 break;
357 }
358
359 case 2:
360 {
361 if (!Fast486LoadSegment(State, FAST486_REG_SS, Tss.Ss2))
362 {
363 /* Exception occurred */
364 return FALSE;
365 }
366 State->GeneralRegs[FAST486_REG_ESP].Long = Tss.Esp2;
367
368 break;
369 }
370
371 default:
372 {
373 /* Should never reach here! */
374 ASSERT(FALSE);
375 }
376 }
377 }
378
379 /* Load new CS */
380 if (!Fast486LoadSegment(State, FAST486_REG_CS, IdtEntry->Selector))
381 {
382 /* An exception occurred during the jump */
383 return FALSE;
384 }
385
386 if (GateSize)
387 {
388 /* 32-bit code segment, use EIP */
389 State->InstPtr.Long = MAKELONG(IdtEntry->Offset, IdtEntry->OffsetHigh);
390 }
391 else
392 {
393 /* 16-bit code segment, use IP */
394 State->InstPtr.LowWord = IdtEntry->Offset;
395 }
396
397 /* Check if the interrupt handler is more privileged or we're in VM86 mode (again) */
398 if ((OldCpl > GET_SEGMENT_RPL(IdtEntry->Selector)) || State->Flags.Vm)
399 {
400 if (State->Flags.Vm)
401 {
402 /* Clear the VM flag */
403 State->Flags.Vm = FALSE;
404
405 /* Push GS, FS, DS and ES */
406 if (!Fast486StackPush(State, State->SegmentRegs[FAST486_REG_GS].Selector)) return FALSE;
407 if (!Fast486StackPush(State, State->SegmentRegs[FAST486_REG_FS].Selector)) return FALSE;
408 if (!Fast486StackPush(State, State->SegmentRegs[FAST486_REG_DS].Selector)) return FALSE;
409 if (!Fast486StackPush(State, State->SegmentRegs[FAST486_REG_ES].Selector)) return FALSE;
410
411 /* Now load them with NULL selectors, since they are useless in protected mode */
412 if (!Fast486LoadSegment(State, FAST486_REG_GS, 0)) return FALSE;
413 if (!Fast486LoadSegment(State, FAST486_REG_FS, 0)) return FALSE;
414 if (!Fast486LoadSegment(State, FAST486_REG_DS, 0)) return FALSE;
415 if (!Fast486LoadSegment(State, FAST486_REG_ES, 0)) return FALSE;
416 }
417
418 /* Push SS selector */
419 if (!Fast486StackPushInternal(State, GateSize, OldSs)) return FALSE;
420
421 /* Push the stack pointer */
422 if (!Fast486StackPushInternal(State, GateSize, OldEsp)) return FALSE;
423 }
424 }
425 else
426 {
427 /* Load new CS */
428 if (!Fast486LoadSegment(State, FAST486_REG_CS, IdtEntry->Selector))
429 {
430 /* An exception occurred during the jump */
431 return FALSE;
432 }
433
434 /* Set the new IP */
435 State->InstPtr.LowWord = IdtEntry->Offset;
436 }
437
438 /* Push EFLAGS */
439 if (!Fast486StackPushInternal(State, GateSize, OldFlags)) return FALSE;
440
441 /* Push CS selector */
442 if (!Fast486StackPushInternal(State, GateSize, OldCs)) return FALSE;
443
444 /* Push the instruction pointer */
445 if (!Fast486StackPushInternal(State, GateSize, OldEip)) return FALSE;
446
447 if (PushErrorCode)
448 {
449 /* Push the error code */
450 if (!Fast486StackPushInternal(State, GateSize, ErrorCode)) return FALSE;
451 }
452
453 if ((IdtEntry->Type == FAST486_IDT_INT_GATE)
454 || (IdtEntry->Type == FAST486_IDT_INT_GATE_32))
455 {
456 /* Disable interrupts after a jump to an interrupt gate handler */
457 State->Flags.If = FALSE;
458 }
459
460 return TRUE;
461 }
462
463 BOOLEAN
464 FASTCALL
465 Fast486PerformInterrupt(PFAST486_STATE State,
466 UCHAR Number)
467 {
468 FAST486_IDT_ENTRY IdtEntry;
469
470 /* Get the interrupt vector */
471 if (!Fast486GetIntVector(State, Number, &IdtEntry))
472 {
473 /* Exception occurred */
474 return FALSE;
475 }
476
477 /* Perform the interrupt */
478 if (!Fast486InterruptInternal(State, &IdtEntry, FALSE, 0))
479 {
480 /* Exception occurred */
481 return FALSE;
482 }
483
484 return TRUE;
485 }
486
487 VOID
488 FASTCALL
489 Fast486ExceptionWithErrorCode(PFAST486_STATE State,
490 FAST486_EXCEPTIONS ExceptionCode,
491 ULONG ErrorCode)
492 {
493 FAST486_IDT_ENTRY IdtEntry;
494
495 /* Increment the exception count */
496 State->ExceptionCount++;
497
498 /* Check if the exception occurred more than once */
499 if (State->ExceptionCount > 1)
500 {
501 /* Then this is a double fault */
502 ExceptionCode = FAST486_EXCEPTION_DF;
503 }
504
505 /* Check if this is a triple fault */
506 if (State->ExceptionCount == 3)
507 {
508 DPRINT("Fast486ExceptionWithErrorCode(%04X:%08X) -- Triple fault\n",
509 State->SegmentRegs[FAST486_REG_CS].Selector,
510 State->InstPtr.Long);
511
512 /* Reset the CPU */
513 Fast486Reset(State);
514 return;
515 }
516
517 /* Clear the prefix flags */
518 State->PrefixFlags = 0;
519
520 /* Restore the IP to the saved IP */
521 State->InstPtr = State->SavedInstPtr;
522
523 /* Get the interrupt vector */
524 if (!Fast486GetIntVector(State, ExceptionCode, &IdtEntry))
525 {
526 /*
527 * If this function failed, that means Fast486Exception
528 * was called again, so just return in this case.
529 */
530 return;
531 }
532
533 /* Perform the interrupt */
534 if (!Fast486InterruptInternal(State,
535 &IdtEntry,
536 EXCEPTION_HAS_ERROR_CODE(ExceptionCode)
537 && (State->ControlRegisters[FAST486_REG_CR0] & FAST486_CR0_PE),
538 ErrorCode))
539 {
540 /*
541 * If this function failed, that means Fast486Exception
542 * was called again, so just return in this case.
543 */
544 return;
545 }
546
547 /* Reset the exception count */
548 State->ExceptionCount = 0;
549 }
550
551 BOOLEAN
552 FASTCALL
553 Fast486TaskSwitch(PFAST486_STATE State, FAST486_TASK_SWITCH_TYPE Type, USHORT Selector)
554 {
555 ULONG NewTssAddress;
556 ULONG NewTssLimit;
557 FAST486_SYSTEM_DESCRIPTOR NewTssDescriptor;
558 FAST486_TSS OldTss;
559 PFAST486_LEGACY_TSS OldLegacyTss = (PFAST486_LEGACY_TSS)&OldTss;
560 FAST486_TSS NewTss;
561 PFAST486_LEGACY_TSS NewLegacyTss = (PFAST486_LEGACY_TSS)&NewTss;
562 USHORT NewLdtr, NewEs, NewCs, NewSs, NewDs;
563
564 if (State->TaskReg.Limit < sizeof(FAST486_TSS)
565 && State->TaskReg.Limit != sizeof(FAST486_LEGACY_TSS))
566 {
567 /* Invalid task register limit */
568 Fast486ExceptionWithErrorCode(State, FAST486_EXCEPTION_TS, State->TaskReg.Selector);
569 return FALSE;
570 }
571
572 /* Read the old TSS */
573 if (!Fast486ReadLinearMemory(State,
574 State->TaskReg.Base,
575 &OldTss,
576 State->TaskReg.Limit >= sizeof(FAST486_TSS)
577 ? sizeof(FAST486_TSS) : sizeof(FAST486_LEGACY_TSS)))
578 {
579 /* Exception occurred */
580 return FALSE;
581 }
582
583
584 /* If this is a task return, use the linked previous selector */
585 if (Type == FAST486_TASK_RETURN)
586 {
587 if (State->TaskReg.Limit >= sizeof(FAST486_TSS)) Selector = LOWORD(OldTss.Link);
588 else Selector = OldLegacyTss->Link;
589 }
590
591 /* Make sure the entry exists in the GDT (not LDT!) */
592 if ((GET_SEGMENT_INDEX(Selector) == 0)
593 || (Selector & SEGMENT_TABLE_INDICATOR)
594 || GET_SEGMENT_INDEX(Selector) >= (State->Gdtr.Size + 1u))
595 {
596 Fast486ExceptionWithErrorCode(State, FAST486_EXCEPTION_TS, Selector);
597 return FALSE;
598 }
599
600 /* Get the TSS descriptor from the GDT */
601 if (!Fast486ReadLinearMemory(State,
602 State->Gdtr.Address + GET_SEGMENT_INDEX(Selector),
603 &NewTssDescriptor,
604 sizeof(NewTssDescriptor)))
605 {
606 /* Exception occurred */
607 return FALSE;
608 }
609
610 if (!NewTssDescriptor.Present)
611 {
612 /* Incoming task TSS not present */
613 Fast486ExceptionWithErrorCode(State, FAST486_EXCEPTION_NP, Selector);
614 return FALSE;
615 }
616
617 /* Calculate the linear address of the new TSS */
618 NewTssAddress = NewTssDescriptor.Base;
619 NewTssAddress |= NewTssDescriptor.BaseMid << 16;
620 NewTssAddress |= NewTssDescriptor.BaseHigh << 24;
621
622 /* Calculate the limit of the new TSS */
623 NewTssLimit = NewTssDescriptor.Limit | (NewTssDescriptor.LimitHigh << 16);
624
625 if (NewTssDescriptor.Granularity)
626 {
627 NewTssLimit <<= 12;
628 NewTssLimit |= 0x00000FFF;
629 }
630
631 if (NewTssLimit < sizeof(FAST486_TSS) && NewTssLimit != sizeof(FAST486_LEGACY_TSS))
632 {
633 /* TSS limit invalid */
634 Fast486ExceptionWithErrorCode(State, FAST486_EXCEPTION_TS, Selector);
635 return FALSE;
636 }
637
638 /*
639 * The incoming task shouldn't be busy if we're executing it as a
640 * new task, and it should be busy if we're returning to it.
641 */
642 if ((((NewTssDescriptor.Signature != FAST486_TSS_SIGNATURE)
643 && (NewTssDescriptor.Signature != FAST486_TSS_16_SIGNATURE))
644 || (Type == FAST486_TASK_RETURN))
645 && (((NewTssDescriptor.Signature != FAST486_BUSY_TSS_SIGNATURE)
646 && (NewTssDescriptor.Signature != FAST486_BUSY_TSS_16_SIGNATURE))
647 || (Type != FAST486_TASK_RETURN)))
648 {
649 Fast486ExceptionWithErrorCode(State, FAST486_EXCEPTION_GP, Selector);
650 return FALSE;
651 }
652
653 /* Read the new TSS */
654 if (!Fast486ReadLinearMemory(State,
655 NewTssAddress,
656 &NewTss,
657 NewTssLimit >= sizeof(FAST486_TSS)
658 ? sizeof(FAST486_TSS) : sizeof(FAST486_LEGACY_TSS)))
659 {
660 /* Exception occurred */
661 return FALSE;
662 }
663
664 if (Type != FAST486_TASK_CALL)
665 {
666 /* Clear the busy bit of the outgoing task */
667 FAST486_SYSTEM_DESCRIPTOR OldTssDescriptor;
668
669 if (!Fast486ReadLinearMemory(State,
670 State->Gdtr.Address
671 + GET_SEGMENT_INDEX(State->TaskReg.Selector),
672 &OldTssDescriptor,
673 sizeof(OldTssDescriptor)))
674 {
675 /* Exception occurred */
676 return FALSE;
677 }
678
679 OldTssDescriptor.Signature = FAST486_TSS_SIGNATURE;
680
681 if (!Fast486WriteLinearMemory(State,
682 State->Gdtr.Address
683 + GET_SEGMENT_INDEX(State->TaskReg.Selector),
684 &OldTssDescriptor,
685 sizeof(OldTssDescriptor)))
686 {
687 /* Exception occurred */
688 return FALSE;
689 }
690 }
691 else
692 {
693 /* Store the link */
694 if (NewTssLimit >= sizeof(FAST486_TSS)) NewTss.Link = State->TaskReg.Selector;
695 else NewLegacyTss->Link = State->TaskReg.Selector;
696 }
697
698 /* Save the current task into the TSS */
699 if (State->TaskReg.Limit >= sizeof(FAST486_TSS))
700 {
701 OldTss.Cr3 = State->ControlRegisters[FAST486_REG_CR3];
702 OldTss.Eip = State->InstPtr.Long;
703 OldTss.Eflags = State->Flags.Long;
704 OldTss.Eax = State->GeneralRegs[FAST486_REG_EAX].Long;
705 OldTss.Ecx = State->GeneralRegs[FAST486_REG_ECX].Long;
706 OldTss.Edx = State->GeneralRegs[FAST486_REG_EDX].Long;
707 OldTss.Ebx = State->GeneralRegs[FAST486_REG_EBX].Long;
708 OldTss.Esp = State->GeneralRegs[FAST486_REG_ESP].Long;
709 OldTss.Ebp = State->GeneralRegs[FAST486_REG_EBP].Long;
710 OldTss.Esi = State->GeneralRegs[FAST486_REG_ESI].Long;
711 OldTss.Edi = State->GeneralRegs[FAST486_REG_EDI].Long;
712 OldTss.Es = State->SegmentRegs[FAST486_REG_ES].Selector;
713 OldTss.Cs = State->SegmentRegs[FAST486_REG_CS].Selector;
714 OldTss.Ss = State->SegmentRegs[FAST486_REG_SS].Selector;
715 OldTss.Ds = State->SegmentRegs[FAST486_REG_DS].Selector;
716 OldTss.Fs = State->SegmentRegs[FAST486_REG_FS].Selector;
717 OldTss.Gs = State->SegmentRegs[FAST486_REG_GS].Selector;
718 OldTss.Ldtr = State->Ldtr.Selector;
719 }
720 else
721 {
722 OldLegacyTss->Ip = State->InstPtr.LowWord;
723 OldLegacyTss->Flags = State->Flags.LowWord;
724 OldLegacyTss->Ax = State->GeneralRegs[FAST486_REG_EAX].LowWord;
725 OldLegacyTss->Cx = State->GeneralRegs[FAST486_REG_ECX].LowWord;
726 OldLegacyTss->Dx = State->GeneralRegs[FAST486_REG_EDX].LowWord;
727 OldLegacyTss->Bx = State->GeneralRegs[FAST486_REG_EBX].LowWord;
728 OldLegacyTss->Sp = State->GeneralRegs[FAST486_REG_ESP].LowWord;
729 OldLegacyTss->Bp = State->GeneralRegs[FAST486_REG_EBP].LowWord;
730 OldLegacyTss->Si = State->GeneralRegs[FAST486_REG_ESI].LowWord;
731 OldLegacyTss->Di = State->GeneralRegs[FAST486_REG_EDI].LowWord;
732 OldLegacyTss->Es = State->SegmentRegs[FAST486_REG_ES].Selector;
733 OldLegacyTss->Cs = State->SegmentRegs[FAST486_REG_CS].Selector;
734 OldLegacyTss->Ss = State->SegmentRegs[FAST486_REG_SS].Selector;
735 OldLegacyTss->Ds = State->SegmentRegs[FAST486_REG_DS].Selector;
736 OldLegacyTss->Ldtr = State->Ldtr.Selector;
737 }
738
739 /* Write back the old TSS */
740 if (!Fast486WriteLinearMemory(State,
741 State->TaskReg.Base,
742 &OldTss,
743 State->TaskReg.Limit >= sizeof(FAST486_TSS)
744 ? sizeof(FAST486_TSS) : sizeof(FAST486_LEGACY_TSS)))
745 {
746 /* Exception occurred */
747 return FALSE;
748 }
749
750 /* Mark the new task as busy */
751 NewTssDescriptor.Signature = FAST486_BUSY_TSS_SIGNATURE;
752
753 /* Write back the new TSS descriptor */
754 if (!Fast486WriteLinearMemory(State,
755 State->Gdtr.Address + GET_SEGMENT_INDEX(Selector),
756 &NewTssDescriptor,
757 sizeof(NewTssDescriptor)))
758 {
759 /* Exception occurred */
760 return FALSE;
761 }
762
763 /* Set the task switch bit */
764 State->ControlRegisters[FAST486_REG_CR0] |= FAST486_CR0_TS;
765
766 /* Load the task register with the new values */
767 State->TaskReg.Selector = Selector;
768 State->TaskReg.Base = NewTssAddress;
769 State->TaskReg.Limit = NewTssLimit;
770
771 if (NewTssLimit >= sizeof(FAST486_TSS))
772 {
773 /* Change the page directory */
774 State->ControlRegisters[FAST486_REG_CR3] = NewTss.Cr3;
775 }
776
777 /* Flush the TLB */
778 if (State->Tlb) RtlZeroMemory(State->Tlb, NUM_TLB_ENTRIES * sizeof(ULONG));
779
780 /* Update the CPL */
781 if (NewTssLimit >= sizeof(FAST486_TSS)) State->Cpl = GET_SEGMENT_RPL(NewTss.Cs);
782 else State->Cpl = GET_SEGMENT_RPL(NewLegacyTss->Cs);
783
784 #ifndef FAST486_NO_PREFETCH
785 /* Context switching invalidates the prefetch */
786 State->PrefetchValid = FALSE;
787 #endif
788
789 /* Load the registers */
790 if (NewTssLimit >= sizeof(FAST486_TSS))
791 {
792 State->InstPtr.Long = State->SavedInstPtr.Long = NewTss.Eip;
793 State->Flags.Long = NewTss.Eflags;
794 State->GeneralRegs[FAST486_REG_EAX].Long = NewTss.Eax;
795 State->GeneralRegs[FAST486_REG_ECX].Long = NewTss.Ecx;
796 State->GeneralRegs[FAST486_REG_EDX].Long = NewTss.Edx;
797 State->GeneralRegs[FAST486_REG_EBX].Long = NewTss.Ebx;
798 State->GeneralRegs[FAST486_REG_ESP].Long = NewTss.Esp;
799 State->GeneralRegs[FAST486_REG_EBP].Long = NewTss.Ebp;
800 State->GeneralRegs[FAST486_REG_ESI].Long = NewTss.Esi;
801 State->GeneralRegs[FAST486_REG_EDI].Long = NewTss.Edi;
802 NewEs = NewTss.Es;
803 NewCs = NewTss.Cs;
804 NewSs = NewTss.Ss;
805 NewDs = NewTss.Ds;
806 NewLdtr = NewTss.Ldtr;
807 }
808 else
809 {
810 State->InstPtr.LowWord = State->SavedInstPtr.LowWord = NewLegacyTss->Ip;
811 State->Flags.LowWord = NewLegacyTss->Flags;
812 State->GeneralRegs[FAST486_REG_EAX].LowWord = NewLegacyTss->Ax;
813 State->GeneralRegs[FAST486_REG_ECX].LowWord = NewLegacyTss->Cx;
814 State->GeneralRegs[FAST486_REG_EDX].LowWord = NewLegacyTss->Dx;
815 State->GeneralRegs[FAST486_REG_EBX].LowWord = NewLegacyTss->Bx;
816 State->GeneralRegs[FAST486_REG_ESP].LowWord = NewLegacyTss->Sp;
817 State->GeneralRegs[FAST486_REG_EBP].LowWord = NewLegacyTss->Bp;
818 State->GeneralRegs[FAST486_REG_ESI].LowWord = NewLegacyTss->Si;
819 State->GeneralRegs[FAST486_REG_EDI].LowWord = NewLegacyTss->Di;
820 NewEs = NewLegacyTss->Es;
821 NewCs = NewLegacyTss->Cs;
822 NewSs = NewLegacyTss->Ss;
823 NewDs = NewLegacyTss->Ds;
824 NewLdtr = NewLegacyTss->Ldtr;
825 }
826
827 /* Set the NT flag if nesting */
828 if (Type == FAST486_TASK_CALL) State->Flags.Nt = TRUE;
829
830 if (GET_SEGMENT_INDEX(NewLdtr) != 0)
831 {
832 BOOLEAN Valid;
833 FAST486_SYSTEM_DESCRIPTOR GdtEntry;
834
835 if (NewLdtr & SEGMENT_TABLE_INDICATOR)
836 {
837 /* This selector doesn't point to the GDT */
838 Fast486ExceptionWithErrorCode(State, FAST486_EXCEPTION_TS, NewLdtr);
839 return FALSE;
840 }
841
842 if (!Fast486ReadDescriptorEntry(State, NewLdtr, &Valid, (PFAST486_GDT_ENTRY)&GdtEntry))
843 {
844 /* Exception occurred */
845 return FALSE;
846 }
847
848 if (!Valid)
849 {
850 /* Invalid selector */
851 Fast486ExceptionWithErrorCode(State, FAST486_EXCEPTION_TS, NewLdtr);
852 return FALSE;
853 }
854
855 if (GdtEntry.Signature != FAST486_LDT_SIGNATURE)
856 {
857 /* This is not an LDT descriptor */
858 Fast486ExceptionWithErrorCode(State, FAST486_EXCEPTION_TS, NewLdtr);
859 return FALSE;
860 }
861
862 if (!GdtEntry.Present)
863 {
864 Fast486ExceptionWithErrorCode(State, FAST486_EXCEPTION_TS, NewLdtr);
865 return FALSE;
866 }
867
868 /* Update the LDTR */
869 State->Ldtr.Selector = NewLdtr;
870 State->Ldtr.Base = GdtEntry.Base | (GdtEntry.BaseMid << 16) | (GdtEntry.BaseHigh << 24);
871 State->Ldtr.Limit = GdtEntry.Limit | (GdtEntry.LimitHigh << 16);
872
873 if (GdtEntry.Granularity)
874 {
875 State->Ldtr.Limit <<= 12;
876 State->Ldtr.Limit |= 0x00000FFF;
877 }
878 }
879 else
880 {
881 /* The LDT of this task is empty */
882 RtlZeroMemory(&State->Ldtr, sizeof(State->Ldtr));
883 }
884
885 /* Load the new segments */
886 if (!Fast486LoadSegmentInternal(State, FAST486_REG_CS, NewCs, FAST486_EXCEPTION_TS))
887 {
888 return FALSE;
889 }
890
891 if (!Fast486LoadSegmentInternal(State, FAST486_REG_SS, NewSs, FAST486_EXCEPTION_TS))
892 {
893 return FALSE;
894 }
895
896 if (!Fast486LoadSegmentInternal(State, FAST486_REG_ES, NewEs, FAST486_EXCEPTION_TS))
897 {
898 return FALSE;
899 }
900
901 if (!Fast486LoadSegmentInternal(State, FAST486_REG_DS, NewDs, FAST486_EXCEPTION_TS))
902 {
903 return FALSE;
904 }
905
906 if (NewTssLimit >= sizeof(FAST486_TSS))
907 {
908 if (!Fast486LoadSegmentInternal(State,
909 FAST486_REG_FS,
910 NewTss.Fs,
911 FAST486_EXCEPTION_TS))
912 {
913 return FALSE;
914 }
915
916 if (!Fast486LoadSegmentInternal(State,
917 FAST486_REG_GS,
918 NewTss.Gs,
919 FAST486_EXCEPTION_TS))
920 {
921 return FALSE;
922 }
923 }
924
925 return TRUE;
926 }
927
928 BOOLEAN
929 FASTCALL
930 Fast486CallGate(PFAST486_STATE State,
931 PFAST486_CALL_GATE Gate,
932 BOOLEAN Call)
933 {
934 BOOLEAN Valid;
935 FAST486_GDT_ENTRY NewCodeSegment;
936 BOOLEAN GateSize = (Gate->Type == FAST486_CALL_GATE_SIGNATURE);
937 FAST486_TSS Tss;
938 USHORT OldCs = State->SegmentRegs[FAST486_REG_CS].Selector;
939 ULONG OldEip = State->InstPtr.Long;
940 USHORT OldCpl = State->Cpl;
941 USHORT OldSs = State->SegmentRegs[FAST486_REG_SS].Selector;
942 ULONG OldEsp = State->GeneralRegs[FAST486_REG_ESP].Long;
943 ULONG ParamBuffer[32]; /* Maximum possible size - 32 DWORDs */
944 PULONG LongParams = (PULONG)ParamBuffer;
945 PUSHORT ShortParams = (PUSHORT)ParamBuffer;
946
947 if (!Gate->Selector)
948 {
949 /* The code segment is NULL */
950 Fast486Exception(State, FAST486_EXCEPTION_GP);
951 return FALSE;
952 }
953
954 if (!Fast486ReadDescriptorEntry(State, Gate->Selector, &Valid, &NewCodeSegment))
955 {
956 /* Exception occurred */
957 return FALSE;
958 }
959
960 if (!Valid || (NewCodeSegment.Dpl > Fast486GetCurrentPrivLevel(State)))
961 {
962 /* Code segment invalid */
963 Fast486ExceptionWithErrorCode(State, FAST486_EXCEPTION_GP, Gate->Selector);
964 return FALSE;
965 }
966
967 if (Call && Gate->ParamCount)
968 {
969 /* Read the parameters */
970 if (!Fast486ReadMemory(State,
971 FAST486_REG_SS,
972 OldEsp,
973 FALSE,
974 ParamBuffer,
975 Gate->ParamCount * sizeof(ULONG)))
976 {
977 /* Exception occurred */
978 return FALSE;
979 }
980 }
981
982 /* Check if the new code segment is more privileged */
983 if (NewCodeSegment.Dpl < OldCpl)
984 {
985 if (Call)
986 {
987 /* Read the TSS */
988 if (!Fast486ReadLinearMemory(State,
989 State->TaskReg.Base,
990 &Tss,
991 sizeof(Tss)))
992 {
993 /* Exception occurred */
994 return FALSE;
995 }
996
997 /* Switch to the new privilege level */
998 State->Cpl = NewCodeSegment.Dpl;
999
1000 /* Check the new (higher) privilege level */
1001 switch (State->Cpl)
1002 {
1003 case 0:
1004 {
1005 if (!Fast486LoadSegment(State, FAST486_REG_SS, Tss.Ss0))
1006 {
1007 /* Exception occurred */
1008 return FALSE;
1009 }
1010 State->GeneralRegs[FAST486_REG_ESP].Long = Tss.Esp0;
1011
1012 break;
1013 }
1014
1015 case 1:
1016 {
1017 if (!Fast486LoadSegment(State, FAST486_REG_SS, Tss.Ss1))
1018 {
1019 /* Exception occurred */
1020 return FALSE;
1021 }
1022 State->GeneralRegs[FAST486_REG_ESP].Long = Tss.Esp1;
1023
1024 break;
1025 }
1026
1027 case 2:
1028 {
1029 if (!Fast486LoadSegment(State, FAST486_REG_SS, Tss.Ss2))
1030 {
1031 /* Exception occurred */
1032 return FALSE;
1033 }
1034 State->GeneralRegs[FAST486_REG_ESP].Long = Tss.Esp2;
1035
1036 break;
1037 }
1038
1039 default:
1040 {
1041 /* Should never reach here! */
1042 ASSERT(FALSE);
1043 }
1044 }
1045 }
1046 else if (!NewCodeSegment.DirConf)
1047 {
1048 /* This is not allowed for jumps */
1049 Fast486ExceptionWithErrorCode(State, FAST486_EXCEPTION_GP, Gate->Selector);
1050 return FALSE;
1051 }
1052 }
1053
1054 /* Load new CS */
1055 if (!Fast486LoadSegment(State, FAST486_REG_CS, Gate->Selector))
1056 {
1057 /* An exception occurred during the jump */
1058 return FALSE;
1059 }
1060
1061 /* Set the instruction pointer */
1062 if (GateSize) State->InstPtr.Long = MAKELONG(Gate->Offset, Gate->OffsetHigh);
1063 else State->InstPtr.Long = Gate->Offset;
1064
1065 if (Call)
1066 {
1067 INT i;
1068
1069 /* Check if the new code segment is more privileged (again) */
1070 if (NewCodeSegment.Dpl < OldCpl)
1071 {
1072 /* Push SS selector */
1073 if (!Fast486StackPushInternal(State, GateSize, OldSs)) return FALSE;
1074
1075 /* Push stack pointer */
1076 if (!Fast486StackPushInternal(State, GateSize, OldEsp)) return FALSE;
1077 }
1078
1079 /* Push the parameters in reverse order */
1080 for (i = Gate->ParamCount - 1; i >= 0; i--)
1081 {
1082 if (!Fast486StackPushInternal(State,
1083 GateSize,
1084 GateSize ? LongParams[i] : ShortParams[i]))
1085 {
1086 /* Exception occurred */
1087 return FALSE;
1088 }
1089 }
1090
1091 /* Push CS selector */
1092 if (!Fast486StackPushInternal(State, GateSize, OldCs)) return FALSE;
1093
1094 /* Push the instruction pointer */
1095 if (!Fast486StackPushInternal(State, GateSize, OldEip)) return FALSE;
1096 }
1097
1098 return TRUE;
1099 }
1100
1101 /* EOF */