[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 USHORT SegmentSelector = IdtEntry->Selector;
296 ULONG Offset = MAKELONG(IdtEntry->Offset, IdtEntry->OffsetHigh);
297 ULONG GateType = IdtEntry->Type;
298 BOOLEAN GateSize = (GateType == FAST486_IDT_INT_GATE_32) ||
299 (GateType == FAST486_IDT_TRAP_GATE_32);
300
301 BOOLEAN Success = FALSE;
302 ULONG OldPrefixFlags = State->PrefixFlags;
303
304 /* Check for protected mode */
305 if (State->ControlRegisters[FAST486_REG_CR0] & FAST486_CR0_PE)
306 {
307 FAST486_TSS Tss;
308 USHORT OldSs = State->SegmentRegs[FAST486_REG_SS].Selector;
309 ULONG OldEsp = State->GeneralRegs[FAST486_REG_ESP].Long;
310
311 if (GateType == FAST486_TASK_GATE_SIGNATURE)
312 {
313 /* Task call */
314 return Fast486TaskSwitch(State, FAST486_TASK_CALL, IdtEntry->Selector);
315 }
316
317 if (GateSize != (State->SegmentRegs[FAST486_REG_CS].Size))
318 {
319 /* The gate size doesn't match the current operand size, so set the OPSIZE flag. */
320 State->PrefixFlags |= FAST486_PREFIX_OPSIZE;
321 }
322
323 /* Check if the interrupt handler is more privileged or if we're in V86 mode */
324 if ((Fast486GetCurrentPrivLevel(State) > GET_SEGMENT_RPL(SegmentSelector))
325 || State->Flags.Vm)
326 {
327 /* Read the TSS */
328 if (!Fast486ReadLinearMemory(State,
329 State->TaskReg.Base,
330 &Tss,
331 sizeof(Tss)))
332 {
333 /* Exception occurred */
334 goto Cleanup;
335 }
336
337 /* Switch to the new privilege level */
338 State->Cpl = GET_SEGMENT_RPL(SegmentSelector);
339
340 if (State->Flags.Vm)
341 {
342 /* Clear the VM flag */
343 State->Flags.Vm = FALSE;
344
345 /* Push GS, FS, DS and ES */
346 if (!Fast486StackPush(State, State->SegmentRegs[FAST486_REG_GS].Selector)) goto Cleanup;
347 if (!Fast486StackPush(State, State->SegmentRegs[FAST486_REG_FS].Selector)) goto Cleanup;
348 if (!Fast486StackPush(State, State->SegmentRegs[FAST486_REG_DS].Selector)) goto Cleanup;
349 if (!Fast486StackPush(State, State->SegmentRegs[FAST486_REG_ES].Selector)) goto Cleanup;
350
351 /* Now load them with NULL selectors, since they are useless in protected mode */
352 if (!Fast486LoadSegment(State, FAST486_REG_GS, 0)) goto Cleanup;
353 if (!Fast486LoadSegment(State, FAST486_REG_FS, 0)) goto Cleanup;
354 if (!Fast486LoadSegment(State, FAST486_REG_DS, 0)) goto Cleanup;
355 if (!Fast486LoadSegment(State, FAST486_REG_ES, 0)) goto Cleanup;
356 }
357
358 /* Check the new (higher) privilege level */
359 switch (State->Cpl)
360 {
361 case 0:
362 {
363 if (!Fast486LoadSegment(State, FAST486_REG_SS, Tss.Ss0))
364 {
365 /* Exception occurred */
366 goto Cleanup;
367 }
368 State->GeneralRegs[FAST486_REG_ESP].Long = Tss.Esp0;
369
370 break;
371 }
372
373 case 1:
374 {
375 if (!Fast486LoadSegment(State, FAST486_REG_SS, Tss.Ss1))
376 {
377 /* Exception occurred */
378 goto Cleanup;
379 }
380 State->GeneralRegs[FAST486_REG_ESP].Long = Tss.Esp1;
381
382 break;
383 }
384
385 case 2:
386 {
387 if (!Fast486LoadSegment(State, FAST486_REG_SS, Tss.Ss2))
388 {
389 /* Exception occurred */
390 goto Cleanup;
391 }
392 State->GeneralRegs[FAST486_REG_ESP].Long = Tss.Esp2;
393
394 break;
395 }
396
397 default:
398 {
399 /* Should never reach here! */
400 ASSERT(FALSE);
401 }
402 }
403
404 /* Push SS selector */
405 if (!Fast486StackPush(State, OldSs)) goto Cleanup;
406
407 /* Push stack pointer */
408 if (!Fast486StackPush(State, OldEsp)) goto Cleanup;
409 }
410 }
411 else
412 {
413 if (State->SegmentRegs[FAST486_REG_CS].Size)
414 {
415 /* Set OPSIZE, because INT always pushes 16-bit values in real mode */
416 State->PrefixFlags |= FAST486_PREFIX_OPSIZE;
417 }
418 }
419
420 /* Push EFLAGS */
421 if (!Fast486StackPush(State, State->Flags.Long)) goto Cleanup;
422
423 /* Push CS selector */
424 if (!Fast486StackPush(State, State->SegmentRegs[FAST486_REG_CS].Selector)) goto Cleanup;
425
426 /* Push the instruction pointer */
427 if (!Fast486StackPush(State, State->InstPtr.Long)) goto Cleanup;
428
429 if (PushErrorCode)
430 {
431 /* Push the error code */
432 if (!Fast486StackPush(State, ErrorCode))
433 {
434 /* An exception occurred */
435 goto Cleanup;
436 }
437 }
438
439 if ((GateType == FAST486_IDT_INT_GATE) || (GateType == FAST486_IDT_INT_GATE_32))
440 {
441 /* Disable interrupts after a jump to an interrupt gate handler */
442 State->Flags.If = FALSE;
443 }
444
445 /* Load new CS */
446 if (!Fast486LoadSegment(State, FAST486_REG_CS, SegmentSelector))
447 {
448 /* An exception occurred during the jump */
449 goto Cleanup;
450 }
451
452 if (GateSize)
453 {
454 /* 32-bit code segment, use EIP */
455 State->InstPtr.Long = Offset;
456 }
457 else
458 {
459 /* 16-bit code segment, use IP */
460 State->InstPtr.LowWord = LOWORD(Offset);
461 }
462
463 Success = TRUE;
464
465 Cleanup:
466 /* Restore the prefix flags */
467 State->PrefixFlags = OldPrefixFlags;
468
469 return Success;
470 }
471
472 BOOLEAN
473 FASTCALL
474 Fast486PerformInterrupt(PFAST486_STATE State,
475 UCHAR Number)
476 {
477 FAST486_IDT_ENTRY IdtEntry;
478
479 /* Get the interrupt vector */
480 if (!Fast486GetIntVector(State, Number, &IdtEntry))
481 {
482 /* Exception occurred */
483 return FALSE;
484 }
485
486 /* Perform the interrupt */
487 if (!Fast486InterruptInternal(State, &IdtEntry, FALSE, 0))
488 {
489 /* Exception occurred */
490 return FALSE;
491 }
492
493 return TRUE;
494 }
495
496 VOID
497 FASTCALL
498 Fast486ExceptionWithErrorCode(PFAST486_STATE State,
499 FAST486_EXCEPTIONS ExceptionCode,
500 ULONG ErrorCode)
501 {
502 FAST486_IDT_ENTRY IdtEntry;
503
504 /* Increment the exception count */
505 State->ExceptionCount++;
506
507 /* Check if the exception occurred more than once */
508 if (State->ExceptionCount > 1)
509 {
510 /* Then this is a double fault */
511 ExceptionCode = FAST486_EXCEPTION_DF;
512 }
513
514 /* Check if this is a triple fault */
515 if (State->ExceptionCount == 3)
516 {
517 DPRINT("Fast486ExceptionWithErrorCode(%04X:%08X) -- Triple fault\n",
518 State->SegmentRegs[FAST486_REG_CS].Selector,
519 State->InstPtr.Long);
520
521 /* Reset the CPU */
522 Fast486Reset(State);
523 return;
524 }
525
526 /* Clear the prefix flags */
527 State->PrefixFlags = 0;
528
529 /* Restore the IP to the saved IP */
530 State->InstPtr = State->SavedInstPtr;
531
532 /* Get the interrupt vector */
533 if (!Fast486GetIntVector(State, ExceptionCode, &IdtEntry))
534 {
535 /*
536 * If this function failed, that means Fast486Exception
537 * was called again, so just return in this case.
538 */
539 return;
540 }
541
542 /* Perform the interrupt */
543 if (!Fast486InterruptInternal(State,
544 &IdtEntry,
545 EXCEPTION_HAS_ERROR_CODE(ExceptionCode)
546 && (State->ControlRegisters[FAST486_REG_CR0] & FAST486_CR0_PE),
547 ErrorCode))
548 {
549 /*
550 * If this function failed, that means Fast486Exception
551 * was called again, so just return in this case.
552 */
553 return;
554 }
555
556 /* Reset the exception count */
557 State->ExceptionCount = 0;
558 }
559
560 BOOLEAN
561 FASTCALL
562 Fast486TaskSwitch(PFAST486_STATE State, FAST486_TASK_SWITCH_TYPE Type, USHORT Selector)
563 {
564 ULONG NewTssAddress;
565 ULONG NewTssLimit;
566 FAST486_TSS OldTss;
567 FAST486_TSS NewTss;
568 FAST486_SYSTEM_DESCRIPTOR NewTssDescriptor;
569
570 /* Read the old TSS */
571 if (!Fast486ReadLinearMemory(State,
572 State->TaskReg.Base,
573 &OldTss,
574 sizeof(OldTss)))
575 {
576 /* Exception occurred */
577 return FALSE;
578 }
579
580 /* If this is a task return, use the linked previous selector */
581 if (Type == FAST486_TASK_RETURN) Selector = LOWORD(OldTss.Link);
582
583 /* Make sure the entry exists in the GDT (not LDT!) */
584 if ((GET_SEGMENT_INDEX(Selector) == 0)
585 || (Selector & SEGMENT_TABLE_INDICATOR)
586 || GET_SEGMENT_INDEX(Selector) >= (State->Gdtr.Size + 1u))
587 {
588 Fast486ExceptionWithErrorCode(State, FAST486_EXCEPTION_TS, Selector);
589 return FALSE;
590 }
591
592 /* Get the TSS descriptor from the GDT */
593 if (!Fast486ReadLinearMemory(State,
594 State->Gdtr.Address + GET_SEGMENT_INDEX(Selector),
595 &NewTssDescriptor,
596 sizeof(NewTssDescriptor)))
597 {
598 /* Exception occurred */
599 return FALSE;
600 }
601
602 if (!NewTssDescriptor.Present)
603 {
604 /* Incoming task TSS not present */
605 Fast486ExceptionWithErrorCode(State, FAST486_EXCEPTION_NP, Selector);
606 return FALSE;
607 }
608
609 /* Calculate the linear address of the new TSS */
610 NewTssAddress = NewTssDescriptor.Base;
611 NewTssAddress |= NewTssDescriptor.BaseMid << 16;
612 NewTssAddress |= NewTssDescriptor.BaseHigh << 24;
613
614 /* Calculate the limit of the new TSS */
615 NewTssLimit = NewTssDescriptor.Limit | (NewTssDescriptor.LimitHigh << 16);
616
617 if (NewTssDescriptor.Granularity)
618 {
619 NewTssLimit <<= 12;
620 NewTssLimit |= 0x00000FFF;
621 }
622
623 if (NewTssLimit < sizeof(FAST486_TSS))
624 {
625 /* TSS limit too small */
626 Fast486ExceptionWithErrorCode(State, FAST486_EXCEPTION_TS, Selector);
627 return FALSE;
628 }
629
630 /*
631 * The incoming task shouldn't be busy if we're executing it as a
632 * new task, and it should be busy if we're returning to it.
633 */
634 if (((NewTssDescriptor.Signature != FAST486_TSS_SIGNATURE)
635 || (Type == FAST486_TASK_RETURN))
636 && ((NewTssDescriptor.Signature != FAST486_BUSY_TSS_SIGNATURE)
637 || (Type != FAST486_TASK_RETURN)))
638 {
639 Fast486ExceptionWithErrorCode(State, FAST486_EXCEPTION_GP, Selector);
640 return FALSE;
641 }
642
643 /* Read the new TSS */
644 if (!Fast486ReadLinearMemory(State,
645 NewTssAddress,
646 &NewTss,
647 sizeof(NewTss)))
648 {
649 /* Exception occurred */
650 return FALSE;
651 }
652
653 if (Type != FAST486_TASK_CALL)
654 {
655 /* Clear the busy bit of the outgoing task */
656 FAST486_SYSTEM_DESCRIPTOR OldTssDescriptor;
657
658 if (!Fast486ReadLinearMemory(State,
659 State->Gdtr.Address
660 + GET_SEGMENT_INDEX(State->TaskReg.Selector),
661 &OldTssDescriptor,
662 sizeof(OldTssDescriptor)))
663 {
664 /* Exception occurred */
665 return FALSE;
666 }
667
668 OldTssDescriptor.Signature = FAST486_TSS_SIGNATURE;
669
670 if (!Fast486WriteLinearMemory(State,
671 State->Gdtr.Address
672 + GET_SEGMENT_INDEX(State->TaskReg.Selector),
673 &OldTssDescriptor,
674 sizeof(OldTssDescriptor)))
675 {
676 /* Exception occurred */
677 return FALSE;
678 }
679 }
680 else
681 {
682 /* Store the link */
683 NewTss.Link = State->TaskReg.Selector;
684 }
685
686 /* Save the current task into the TSS */
687 OldTss.Cr3 = State->ControlRegisters[FAST486_REG_CR3];
688 OldTss.Eip = State->InstPtr.Long;
689 OldTss.Eflags = State->Flags.Long;
690 OldTss.Eax = State->GeneralRegs[FAST486_REG_EAX].Long;
691 OldTss.Ecx = State->GeneralRegs[FAST486_REG_ECX].Long;
692 OldTss.Edx = State->GeneralRegs[FAST486_REG_EDX].Long;
693 OldTss.Ebx = State->GeneralRegs[FAST486_REG_EBX].Long;
694 OldTss.Esp = State->GeneralRegs[FAST486_REG_ESP].Long;
695 OldTss.Ebp = State->GeneralRegs[FAST486_REG_EBP].Long;
696 OldTss.Esi = State->GeneralRegs[FAST486_REG_ESI].Long;
697 OldTss.Edi = State->GeneralRegs[FAST486_REG_EDI].Long;
698 OldTss.Es = State->SegmentRegs[FAST486_REG_ES].Selector;
699 OldTss.Cs = State->SegmentRegs[FAST486_REG_CS].Selector;
700 OldTss.Ss = State->SegmentRegs[FAST486_REG_SS].Selector;
701 OldTss.Ds = State->SegmentRegs[FAST486_REG_DS].Selector;
702 OldTss.Fs = State->SegmentRegs[FAST486_REG_FS].Selector;
703 OldTss.Gs = State->SegmentRegs[FAST486_REG_GS].Selector;
704 OldTss.Ldtr = State->Ldtr.Selector;
705
706 /* Write back the old TSS */
707 if (!Fast486WriteLinearMemory(State,
708 State->TaskReg.Base,
709 &OldTss,
710 sizeof(OldTss)))
711 {
712 /* Exception occurred */
713 return FALSE;
714 }
715
716 /* Mark the new task as busy */
717 NewTssDescriptor.Signature = FAST486_BUSY_TSS_SIGNATURE;
718
719 /* Write back the new TSS descriptor */
720 if (!Fast486WriteLinearMemory(State,
721 State->Gdtr.Address + GET_SEGMENT_INDEX(Selector),
722 &NewTssDescriptor,
723 sizeof(NewTssDescriptor)))
724 {
725 /* Exception occurred */
726 return FALSE;
727 }
728
729 /* Set the task switch bit */
730 State->ControlRegisters[FAST486_REG_CR0] |= FAST486_CR0_TS;
731
732 /* Load the task register with the new values */
733 State->TaskReg.Selector = Selector;
734 State->TaskReg.Base = NewTssAddress;
735 State->TaskReg.Limit = NewTssLimit;
736
737 /* Change the page directory */
738 State->ControlRegisters[FAST486_REG_CR3] = NewTss.Cr3;
739
740 /* Flush the TLB */
741 if (State->Tlb) RtlZeroMemory(State->Tlb, NUM_TLB_ENTRIES * sizeof(ULONG));
742
743 /* Update the CPL */
744 State->Cpl = GET_SEGMENT_RPL(NewTss.Cs);
745
746 #ifndef FAST486_NO_PREFETCH
747 /* Context switching invalidates the prefetch */
748 State->PrefetchValid = FALSE;
749 #endif
750
751 /* Update the CPL */
752 State->Cpl = GET_SEGMENT_RPL(NewTss.Cs);
753
754 /* Load the registers */
755 State->InstPtr.Long = State->SavedInstPtr.Long = NewTss.Eip;
756 State->Flags.Long = NewTss.Eflags;
757 State->GeneralRegs[FAST486_REG_EAX].Long = NewTss.Eax;
758 State->GeneralRegs[FAST486_REG_ECX].Long = NewTss.Ecx;
759 State->GeneralRegs[FAST486_REG_EDX].Long = NewTss.Edx;
760 State->GeneralRegs[FAST486_REG_EBX].Long = NewTss.Ebx;
761 State->GeneralRegs[FAST486_REG_ESP].Long = NewTss.Esp;
762 State->GeneralRegs[FAST486_REG_EBP].Long = NewTss.Ebp;
763 State->GeneralRegs[FAST486_REG_ESI].Long = NewTss.Esi;
764 State->GeneralRegs[FAST486_REG_EDI].Long = NewTss.Edi;
765
766 /* Set the NT flag if nesting */
767 if (Type == FAST486_TASK_CALL) State->Flags.Nt = TRUE;
768
769 if (GET_SEGMENT_INDEX(NewTss.Ldtr) != 0)
770 {
771 BOOLEAN Valid;
772 FAST486_SYSTEM_DESCRIPTOR GdtEntry;
773
774 if (NewTss.Ldtr & SEGMENT_TABLE_INDICATOR)
775 {
776 /* This selector doesn't point to the GDT */
777 Fast486ExceptionWithErrorCode(State, FAST486_EXCEPTION_TS, NewTss.Ldtr);
778 return FALSE;
779 }
780
781 if (!Fast486ReadDescriptorEntry(State,
782 NewTss.Ldtr,
783 &Valid,
784 (PFAST486_GDT_ENTRY)&GdtEntry))
785 {
786 /* Exception occurred */
787 return FALSE;
788 }
789
790 if (!Valid)
791 {
792 /* Invalid selector */
793 Fast486ExceptionWithErrorCode(State, FAST486_EXCEPTION_TS, NewTss.Ldtr);
794 return FALSE;
795 }
796
797 if (GdtEntry.Signature != FAST486_LDT_SIGNATURE)
798 {
799 /* This is not an LDT descriptor */
800 Fast486ExceptionWithErrorCode(State, FAST486_EXCEPTION_TS, NewTss.Ldtr);
801 return FALSE;
802 }
803
804 if (!GdtEntry.Present)
805 {
806 Fast486ExceptionWithErrorCode(State, FAST486_EXCEPTION_TS, NewTss.Ldtr);
807 return FALSE;
808 }
809
810 /* Update the LDTR */
811 State->Ldtr.Selector = NewTss.Ldtr;
812 State->Ldtr.Base = GdtEntry.Base | (GdtEntry.BaseMid << 16) | (GdtEntry.BaseHigh << 24);
813 State->Ldtr.Limit = GdtEntry.Limit | (GdtEntry.LimitHigh << 16);
814
815 if (GdtEntry.Granularity)
816 {
817 State->Ldtr.Limit <<= 12;
818 State->Ldtr.Limit |= 0x00000FFF;
819 }
820 }
821 else
822 {
823 /* The LDT of this task is empty */
824 RtlZeroMemory(&State->Ldtr, sizeof(State->Ldtr));
825 }
826
827 /* Load the new segments */
828 if (!Fast486LoadSegmentInternal(State,
829 FAST486_REG_CS,
830 NewTss.Cs,
831 FAST486_EXCEPTION_TS))
832 {
833 return FALSE;
834 }
835
836 if (!Fast486LoadSegmentInternal(State,
837 FAST486_REG_SS,
838 NewTss.Ss,
839 FAST486_EXCEPTION_TS))
840 {
841 return FALSE;
842 }
843
844 if (!Fast486LoadSegmentInternal(State,
845 FAST486_REG_ES,
846 NewTss.Es,
847 FAST486_EXCEPTION_TS))
848 {
849 return FALSE;
850 }
851
852 if (!Fast486LoadSegmentInternal(State,
853 FAST486_REG_DS,
854 NewTss.Ds,
855 FAST486_EXCEPTION_TS))
856 {
857 return FALSE;
858 }
859
860 if (!Fast486LoadSegmentInternal(State,
861 FAST486_REG_FS,
862 NewTss.Fs,
863 FAST486_EXCEPTION_TS))
864 {
865 return FALSE;
866 }
867
868 if (!Fast486LoadSegmentInternal(State,
869 FAST486_REG_GS,
870 NewTss.Gs,
871 FAST486_EXCEPTION_TS))
872 {
873 return FALSE;
874 }
875
876 return TRUE;
877 }
878
879 /* EOF */