Partial merge of condrv_restructure branch r65657.
[reactos.git] / reactos / lib / fast486 / common.c
1 /*
2 * Fast486 386/486 CPU Emulation Library
3 * common.c
4 *
5 * Copyright (C) 2014 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 ((Offset + Size - 1) > CachedDescriptor->Limit)
51 {
52 /* Read beyond limit */
53 Fast486Exception(State, FAST486_EXCEPTION_GP);
54 return FALSE;
55 }
56
57 /* Check for protected mode */
58 if (State->ControlRegisters[FAST486_REG_CR0] & FAST486_CR0_PE)
59 {
60 /* Privilege checks */
61
62 if (!CachedDescriptor->Present)
63 {
64 Fast486Exception(State, FAST486_EXCEPTION_NP);
65 return FALSE;
66 }
67
68 if ((!InstFetch && (CachedDescriptor->Rpl > CachedDescriptor->Dpl))
69 || (Fast486GetCurrentPrivLevel(State) > CachedDescriptor->Dpl))
70 {
71 Fast486Exception(State, FAST486_EXCEPTION_GP);
72 return FALSE;
73 }
74
75 if (InstFetch)
76 {
77 if (!CachedDescriptor->Executable)
78 {
79 /* Data segment not executable */
80 Fast486Exception(State, FAST486_EXCEPTION_GP);
81 return FALSE;
82 }
83 }
84 else
85 {
86 if (CachedDescriptor->Executable && (!CachedDescriptor->ReadWrite))
87 {
88 /* Code segment not readable */
89 Fast486Exception(State, FAST486_EXCEPTION_GP);
90 return FALSE;
91 }
92 }
93 }
94
95 /* Find the linear address */
96 LinearAddress = CachedDescriptor->Base + Offset;
97
98 #ifndef FAST486_NO_PREFETCH
99 if (InstFetch && ((Offset + FAST486_CACHE_SIZE - 1) <= CachedDescriptor->Limit))
100 {
101 State->PrefetchAddress = LinearAddress;
102
103 if ((State->ControlRegisters[FAST486_REG_CR0] & FAST486_CR0_PG)
104 && (PAGE_OFFSET(State->PrefetchAddress) > (FAST486_PAGE_SIZE - FAST486_CACHE_SIZE)))
105 {
106 /* We mustn't prefetch across a page boundary */
107 State->PrefetchAddress = PAGE_ALIGN(State->PrefetchAddress)
108 | (FAST486_PAGE_SIZE - FAST486_CACHE_SIZE);
109
110 if ((LinearAddress - State->PrefetchAddress + Size) >= FAST486_CACHE_SIZE)
111 {
112 /* We can't prefetch without possibly violating page permissions */
113 State->PrefetchValid = FALSE;
114 return Fast486ReadLinearMemory(State, LinearAddress, Buffer, Size);
115 }
116 }
117
118 /* Prefetch */
119 if (Fast486ReadLinearMemory(State,
120 State->PrefetchAddress,
121 State->PrefetchCache,
122 FAST486_CACHE_SIZE))
123 {
124 State->PrefetchValid = TRUE;
125
126 RtlMoveMemory(Buffer,
127 &State->PrefetchCache[LinearAddress - State->PrefetchAddress],
128 Size);
129 return TRUE;
130 }
131 else
132 {
133 State->PrefetchValid = FALSE;
134 return FALSE;
135 }
136 }
137 else
138 #endif
139 {
140 /* Read from the linear address */
141 return Fast486ReadLinearMemory(State, LinearAddress, Buffer, Size);
142 }
143 }
144
145 BOOLEAN
146 Fast486WriteMemory(PFAST486_STATE State,
147 FAST486_SEG_REGS SegmentReg,
148 ULONG Offset,
149 PVOID Buffer,
150 ULONG Size)
151 {
152 ULONG LinearAddress;
153 PFAST486_SEG_REG CachedDescriptor;
154
155 ASSERT(SegmentReg < FAST486_NUM_SEG_REGS);
156
157 /* Get the cached descriptor */
158 CachedDescriptor = &State->SegmentRegs[SegmentReg];
159
160 if ((Offset + Size - 1) > CachedDescriptor->Limit)
161 {
162 /* Write beyond limit */
163 Fast486Exception(State, FAST486_EXCEPTION_GP);
164 return FALSE;
165 }
166
167 /* Check for protected mode */
168 if (State->ControlRegisters[FAST486_REG_CR0] & FAST486_CR0_PE)
169 {
170 /* Privilege checks */
171
172 if (!CachedDescriptor->Present)
173 {
174 Fast486Exception(State, FAST486_EXCEPTION_NP);
175 return FALSE;
176 }
177
178 if ((CachedDescriptor->Rpl > CachedDescriptor->Dpl)
179 || (Fast486GetCurrentPrivLevel(State) > CachedDescriptor->Dpl))
180 {
181 Fast486Exception(State, FAST486_EXCEPTION_GP);
182 return FALSE;
183 }
184
185 if (CachedDescriptor->Executable)
186 {
187 /* Code segment not writable */
188 Fast486Exception(State, FAST486_EXCEPTION_GP);
189 return FALSE;
190 }
191 else if (!CachedDescriptor->ReadWrite)
192 {
193 /* Data segment not writeable */
194 Fast486Exception(State, FAST486_EXCEPTION_GP);
195 return FALSE;
196 }
197 }
198
199 /* Find the linear address */
200 LinearAddress = CachedDescriptor->Base + Offset;
201
202 #ifndef FAST486_NO_PREFETCH
203 if (State->PrefetchValid
204 && (LinearAddress >= State->PrefetchAddress)
205 && ((LinearAddress + Size) <= (State->PrefetchAddress + FAST486_CACHE_SIZE)))
206 {
207 /* Update the prefetch */
208 RtlMoveMemory(&State->PrefetchCache[LinearAddress - State->PrefetchAddress],
209 Buffer,
210 min(Size, FAST486_CACHE_SIZE + State->PrefetchAddress - LinearAddress));
211 }
212 #endif
213
214 /* Write to the linear address */
215 return Fast486WriteLinearMemory(State, LinearAddress, Buffer, Size);
216 }
217
218 static inline BOOLEAN
219 FASTCALL
220 Fast486GetIntVector(PFAST486_STATE State,
221 UCHAR Number,
222 PFAST486_IDT_ENTRY IdtEntry)
223 {
224 /* Check for protected mode */
225 if (State->ControlRegisters[FAST486_REG_CR0] & FAST486_CR0_PE)
226 {
227 /* Read from the IDT */
228 if (!Fast486ReadLinearMemory(State,
229 State->Idtr.Address
230 + Number * sizeof(*IdtEntry),
231 IdtEntry,
232 sizeof(*IdtEntry)))
233 {
234 /* Exception occurred */
235 return FALSE;
236 }
237 }
238 else
239 {
240 /* Read from the real-mode IVT */
241 ULONG FarPointer;
242
243 /* Paging is always disabled in real mode */
244 State->MemReadCallback(State,
245 State->Idtr.Address
246 + Number * sizeof(FarPointer),
247 &FarPointer,
248 sizeof(FarPointer));
249
250 /* Fill a fake IDT entry */
251 IdtEntry->Offset = LOWORD(FarPointer);
252 IdtEntry->Selector = HIWORD(FarPointer);
253 IdtEntry->Zero = 0;
254 IdtEntry->Type = FAST486_IDT_INT_GATE;
255 IdtEntry->Storage = FALSE;
256 IdtEntry->Dpl = 0;
257 IdtEntry->Present = TRUE;
258 IdtEntry->OffsetHigh = 0;
259 }
260
261 return TRUE;
262 }
263
264 static inline BOOLEAN
265 FASTCALL
266 Fast486InterruptInternal(PFAST486_STATE State,
267 PFAST486_IDT_ENTRY IdtEntry)
268 {
269 USHORT SegmentSelector = IdtEntry->Selector;
270 ULONG Offset = MAKELONG(IdtEntry->Offset, IdtEntry->OffsetHigh);
271 ULONG GateType = IdtEntry->Type;
272 BOOLEAN GateSize = (GateType == FAST486_IDT_INT_GATE_32) ||
273 (GateType == FAST486_IDT_TRAP_GATE_32);
274
275 BOOLEAN Success = FALSE;
276 ULONG OldPrefixFlags = State->PrefixFlags;
277
278 /* Check for protected mode */
279 if (State->ControlRegisters[FAST486_REG_CR0] & FAST486_CR0_PE)
280 {
281 FAST486_TSS Tss;
282 USHORT OldSs = State->SegmentRegs[FAST486_REG_SS].Selector;
283 ULONG OldEsp = State->GeneralRegs[FAST486_REG_ESP].Long;
284
285 if (GateType == FAST486_TASK_GATE_SIGNATURE)
286 {
287 /* Task call */
288 return Fast486TaskSwitch(State, FAST486_TASK_CALL, IdtEntry->Selector);
289 }
290
291 if (GateSize != (State->SegmentRegs[FAST486_REG_CS].Size))
292 {
293 /* The gate size doesn't match the current operand size, so set the OPSIZE flag. */
294 State->PrefixFlags |= FAST486_PREFIX_OPSIZE;
295 }
296
297 /* Check if the interrupt handler is more privileged or if we're in V86 mode */
298 if ((Fast486GetCurrentPrivLevel(State) > GET_SEGMENT_RPL(SegmentSelector))
299 || State->Flags.Vm)
300 {
301 /* Read the TSS */
302 if (!Fast486ReadLinearMemory(State,
303 State->TaskReg.Base,
304 &Tss,
305 sizeof(Tss)))
306 {
307 /* Exception occurred */
308 goto Cleanup;
309 }
310
311 /* Switch to the new privilege level */
312 State->Cpl = GET_SEGMENT_RPL(SegmentSelector);
313
314 if (State->Flags.Vm)
315 {
316 /* Clear the VM flag */
317 State->Flags.Vm = FALSE;
318
319 /* Push GS, FS, DS and ES */
320 if (!Fast486StackPush(State, State->SegmentRegs[FAST486_REG_GS].Selector)) goto Cleanup;
321 if (!Fast486StackPush(State, State->SegmentRegs[FAST486_REG_FS].Selector)) goto Cleanup;
322 if (!Fast486StackPush(State, State->SegmentRegs[FAST486_REG_DS].Selector)) goto Cleanup;
323 if (!Fast486StackPush(State, State->SegmentRegs[FAST486_REG_ES].Selector)) goto Cleanup;
324
325 /* Now load them with NULL selectors, since they are useless in protected mode */
326 if (!Fast486LoadSegment(State, FAST486_REG_GS, 0)) goto Cleanup;
327 if (!Fast486LoadSegment(State, FAST486_REG_FS, 0)) goto Cleanup;
328 if (!Fast486LoadSegment(State, FAST486_REG_DS, 0)) goto Cleanup;
329 if (!Fast486LoadSegment(State, FAST486_REG_ES, 0)) goto Cleanup;
330 }
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 goto Cleanup;
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 goto Cleanup;
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 goto Cleanup;
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 /* Push SS selector */
379 if (!Fast486StackPush(State, OldSs)) goto Cleanup;
380
381 /* Push stack pointer */
382 if (!Fast486StackPush(State, OldEsp)) goto Cleanup;
383 }
384 }
385 else
386 {
387 if (State->SegmentRegs[FAST486_REG_CS].Size)
388 {
389 /* Set OPSIZE, because INT always pushes 16-bit values in real mode */
390 State->PrefixFlags |= FAST486_PREFIX_OPSIZE;
391 }
392 }
393
394 /* Push EFLAGS */
395 if (!Fast486StackPush(State, State->Flags.Long)) goto Cleanup;
396
397 /* Push CS selector */
398 if (!Fast486StackPush(State, State->SegmentRegs[FAST486_REG_CS].Selector)) goto Cleanup;
399
400 /* Push the instruction pointer */
401 if (!Fast486StackPush(State, State->InstPtr.Long)) goto Cleanup;
402
403 if ((GateType == FAST486_IDT_INT_GATE) || (GateType == FAST486_IDT_INT_GATE_32))
404 {
405 /* Disable interrupts after a jump to an interrupt gate handler */
406 State->Flags.If = FALSE;
407 }
408
409 /* Load new CS */
410 if (!Fast486LoadSegment(State, FAST486_REG_CS, SegmentSelector))
411 {
412 /* An exception occurred during the jump */
413 goto Cleanup;
414 }
415
416 if (GateSize)
417 {
418 /* 32-bit code segment, use EIP */
419 State->InstPtr.Long = Offset;
420 }
421 else
422 {
423 /* 16-bit code segment, use IP */
424 State->InstPtr.LowWord = LOWORD(Offset);
425 }
426
427 Success = TRUE;
428
429 Cleanup:
430 /* Restore the prefix flags */
431 State->PrefixFlags = OldPrefixFlags;
432
433 return Success;
434 }
435
436 BOOLEAN
437 FASTCALL
438 Fast486PerformInterrupt(PFAST486_STATE State,
439 UCHAR Number)
440 {
441 FAST486_IDT_ENTRY IdtEntry;
442
443 /* Get the interrupt vector */
444 if (!Fast486GetIntVector(State, Number, &IdtEntry))
445 {
446 /* Exception occurred */
447 return FALSE;
448 }
449
450 /* Perform the interrupt */
451 if (!Fast486InterruptInternal(State, &IdtEntry))
452 {
453 /* Exception occurred */
454 return FALSE;
455 }
456
457 return TRUE;
458 }
459
460 VOID
461 FASTCALL
462 Fast486ExceptionWithErrorCode(PFAST486_STATE State,
463 FAST486_EXCEPTIONS ExceptionCode,
464 ULONG ErrorCode)
465 {
466 /* Increment the exception count */
467 State->ExceptionCount++;
468
469 /* Check if the exception occurred more than once */
470 if (State->ExceptionCount > 1)
471 {
472 /* Then this is a double fault */
473 ExceptionCode = FAST486_EXCEPTION_DF;
474 }
475
476 /* Check if this is a triple fault */
477 if (State->ExceptionCount == 3)
478 {
479 DPRINT("Fast486ExceptionWithErrorCode(%04X:%08X) -- Triple fault\n",
480 State->SegmentRegs[FAST486_REG_CS].Selector,
481 State->InstPtr.Long);
482
483 /* Reset the CPU */
484 Fast486Reset(State);
485 return;
486 }
487
488 /* Clear the prefix flags */
489 State->PrefixFlags = 0;
490
491 /* Restore the IP to the saved IP */
492 State->InstPtr = State->SavedInstPtr;
493
494 /* Perform the interrupt */
495 if (!Fast486PerformInterrupt(State, ExceptionCode))
496 {
497 /*
498 * If this function failed, that means Fast486Exception
499 * was called again, so just return in this case.
500 */
501 return;
502 }
503
504 if (EXCEPTION_HAS_ERROR_CODE(ExceptionCode)
505 && (State->ControlRegisters[FAST486_REG_CR0] & FAST486_CR0_PE))
506 {
507 /* Push the error code */
508 if (!Fast486StackPush(State, ErrorCode))
509 {
510 /*
511 * If this function failed, that means Fast486Exception
512 * was called again, so just return in this case.
513 */
514 return;
515 }
516 }
517
518 /* Reset the exception count */
519 State->ExceptionCount = 0;
520 }
521
522 BOOLEAN
523 FASTCALL
524 Fast486TaskSwitch(PFAST486_STATE State, FAST486_TASK_SWITCH_TYPE Type, USHORT Selector)
525 {
526 ULONG NewTssAddress;
527 ULONG NewTssLimit;
528 FAST486_TSS OldTss;
529 FAST486_TSS NewTss;
530 FAST486_SYSTEM_DESCRIPTOR NewTssDescriptor;
531
532 /* Read the old TSS */
533 if (!Fast486ReadLinearMemory(State,
534 State->TaskReg.Base,
535 &OldTss,
536 sizeof(OldTss)))
537 {
538 /* Exception occurred */
539 return FALSE;
540 }
541
542 /* If this is a task return, use the linked previous selector */
543 if (Type == FAST486_TASK_RETURN) Selector = LOWORD(OldTss.Link);
544
545 /* Make sure the entry exists in the GDT (not LDT!) */
546 if ((GET_SEGMENT_INDEX(Selector) == 0)
547 || (Selector & SEGMENT_TABLE_INDICATOR)
548 || GET_SEGMENT_INDEX(Selector) >= (State->Gdtr.Size + 1))
549 {
550 Fast486ExceptionWithErrorCode(State, FAST486_EXCEPTION_TS, Selector);
551 return FALSE;
552 }
553
554 /* Get the TSS descriptor from the GDT */
555 if (!Fast486ReadLinearMemory(State,
556 State->Gdtr.Address + GET_SEGMENT_INDEX(Selector),
557 &NewTssDescriptor,
558 sizeof(NewTssDescriptor)))
559 {
560 /* Exception occurred */
561 return FALSE;
562 }
563
564 if (!NewTssDescriptor.Present)
565 {
566 /* Incoming task TSS not present */
567 Fast486ExceptionWithErrorCode(State, FAST486_EXCEPTION_NP, Selector);
568 return FALSE;
569 }
570
571 /* Calculate the linear address of the new TSS */
572 NewTssAddress = NewTssDescriptor.Base;
573 NewTssAddress |= NewTssDescriptor.BaseMid << 16;
574 NewTssAddress |= NewTssDescriptor.BaseHigh << 24;
575
576 /* Calculate the limit of the new TSS */
577 NewTssLimit = NewTssDescriptor.Limit | (NewTssDescriptor.LimitHigh << 16);
578
579 if (NewTssDescriptor.Granularity)
580 {
581 NewTssLimit <<= 12;
582 NewTssLimit |= 0x00000FFF;
583 }
584
585 if (NewTssLimit < sizeof(FAST486_TSS))
586 {
587 /* TSS limit too small */
588 Fast486ExceptionWithErrorCode(State, FAST486_EXCEPTION_TS, Selector);
589 return FALSE;
590 }
591
592 /*
593 * The incoming task shouldn't be busy if we're executing it as a
594 * new task, and it should be busy if we're returning to it.
595 */
596 if (((NewTssDescriptor.Signature != FAST486_TSS_SIGNATURE)
597 || (Type == FAST486_TASK_RETURN))
598 && ((NewTssDescriptor.Signature != FAST486_BUSY_TSS_SIGNATURE)
599 || (Type != FAST486_TASK_RETURN)))
600 {
601 Fast486ExceptionWithErrorCode(State, FAST486_EXCEPTION_GP, Selector);
602 return FALSE;
603 }
604
605 /* Read the new TSS */
606 if (!Fast486ReadLinearMemory(State,
607 NewTssAddress,
608 &NewTss,
609 sizeof(NewTss)))
610 {
611 /* Exception occurred */
612 return FALSE;
613 }
614
615 if (Type != FAST486_TASK_CALL)
616 {
617 /* Clear the busy bit of the outgoing task */
618 FAST486_SYSTEM_DESCRIPTOR OldTssDescriptor;
619
620 if (!Fast486ReadLinearMemory(State,
621 State->Gdtr.Address
622 + GET_SEGMENT_INDEX(State->TaskReg.Selector),
623 &OldTssDescriptor,
624 sizeof(OldTssDescriptor)))
625 {
626 /* Exception occurred */
627 return FALSE;
628 }
629
630 OldTssDescriptor.Signature = FAST486_TSS_SIGNATURE;
631
632 if (!Fast486WriteLinearMemory(State,
633 State->Gdtr.Address
634 + GET_SEGMENT_INDEX(State->TaskReg.Selector),
635 &OldTssDescriptor,
636 sizeof(OldTssDescriptor)))
637 {
638 /* Exception occurred */
639 return FALSE;
640 }
641 }
642 else
643 {
644 /* Store the link */
645 NewTss.Link = State->TaskReg.Selector;
646 }
647
648 /* Save the current task into the TSS */
649 OldTss.Cr3 = State->ControlRegisters[FAST486_REG_CR3];
650 OldTss.Eip = State->InstPtr.Long;
651 OldTss.Eflags = State->Flags.Long;
652 OldTss.Eax = State->GeneralRegs[FAST486_REG_EAX].Long;
653 OldTss.Ecx = State->GeneralRegs[FAST486_REG_ECX].Long;
654 OldTss.Edx = State->GeneralRegs[FAST486_REG_EDX].Long;
655 OldTss.Ebx = State->GeneralRegs[FAST486_REG_EBX].Long;
656 OldTss.Esp = State->GeneralRegs[FAST486_REG_ESP].Long;
657 OldTss.Ebp = State->GeneralRegs[FAST486_REG_EBP].Long;
658 OldTss.Esi = State->GeneralRegs[FAST486_REG_ESI].Long;
659 OldTss.Edi = State->GeneralRegs[FAST486_REG_EDI].Long;
660 OldTss.Es = State->SegmentRegs[FAST486_REG_ES].Selector;
661 OldTss.Cs = State->SegmentRegs[FAST486_REG_CS].Selector;
662 OldTss.Ss = State->SegmentRegs[FAST486_REG_SS].Selector;
663 OldTss.Ds = State->SegmentRegs[FAST486_REG_DS].Selector;
664 OldTss.Fs = State->SegmentRegs[FAST486_REG_FS].Selector;
665 OldTss.Gs = State->SegmentRegs[FAST486_REG_GS].Selector;
666 OldTss.Ldtr = State->Ldtr.Selector;
667
668 /* Write back the old TSS */
669 if (!Fast486WriteLinearMemory(State,
670 State->TaskReg.Base,
671 &OldTss,
672 sizeof(OldTss)))
673 {
674 /* Exception occurred */
675 return FALSE;
676 }
677
678 /* Mark the new task as busy */
679 NewTssDescriptor.Signature = FAST486_BUSY_TSS_SIGNATURE;
680
681 /* Write back the new TSS descriptor */
682 if (!Fast486WriteLinearMemory(State,
683 State->Gdtr.Address + GET_SEGMENT_INDEX(Selector),
684 &NewTssDescriptor,
685 sizeof(NewTssDescriptor)))
686 {
687 /* Exception occurred */
688 return FALSE;
689 }
690
691 /* Set the task switch bit */
692 State->ControlRegisters[FAST486_REG_CR0] |= FAST486_CR0_TS;
693
694 /* Load the task register with the new values */
695 State->TaskReg.Selector = Selector;
696 State->TaskReg.Base = NewTssAddress;
697 State->TaskReg.Limit = NewTssLimit;
698
699 /* Change the page directory */
700 State->ControlRegisters[FAST486_REG_CR3] = NewTss.Cr3;
701
702 /* Flush the TLB */
703 if (State->Tlb) RtlZeroMemory(State->Tlb, NUM_TLB_ENTRIES * sizeof(ULONG));
704
705 #ifndef FAST486_NO_PREFETCH
706 /* Context switching invalidates the prefetch */
707 State->PrefetchValid = FALSE;
708 #endif
709
710 /* Load the registers */
711 State->InstPtr.Long = State->SavedInstPtr.Long = NewTss.Eip;
712 State->Flags.Long = NewTss.Eflags;
713 State->GeneralRegs[FAST486_REG_EAX].Long = NewTss.Eax;
714 State->GeneralRegs[FAST486_REG_ECX].Long = NewTss.Ecx;
715 State->GeneralRegs[FAST486_REG_EDX].Long = NewTss.Edx;
716 State->GeneralRegs[FAST486_REG_EBX].Long = NewTss.Ebx;
717 State->GeneralRegs[FAST486_REG_ESP].Long = NewTss.Esp;
718 State->GeneralRegs[FAST486_REG_EBP].Long = NewTss.Ebp;
719 State->GeneralRegs[FAST486_REG_ESI].Long = NewTss.Esi;
720 State->GeneralRegs[FAST486_REG_EDI].Long = NewTss.Edi;
721
722 /* Set the NT flag if nesting */
723 if (Type == FAST486_TASK_CALL) State->Flags.Nt = TRUE;
724
725 if (GET_SEGMENT_INDEX(NewTss.Ldtr) != 0)
726 {
727 BOOLEAN Valid;
728 FAST486_SYSTEM_DESCRIPTOR GdtEntry;
729
730 if (NewTss.Ldtr & SEGMENT_TABLE_INDICATOR)
731 {
732 /* This selector doesn't point to the GDT */
733 Fast486ExceptionWithErrorCode(State, FAST486_EXCEPTION_TS, NewTss.Ldtr);
734 return FALSE;
735 }
736
737 if (!Fast486ReadDescriptorEntry(State,
738 NewTss.Ldtr,
739 &Valid,
740 (PFAST486_GDT_ENTRY)&GdtEntry))
741 {
742 /* Exception occurred */
743 return FALSE;
744 }
745
746 if (!Valid)
747 {
748 /* Invalid selector */
749 Fast486ExceptionWithErrorCode(State, FAST486_EXCEPTION_TS, NewTss.Ldtr);
750 return FALSE;
751 }
752
753 if (GdtEntry.Signature != FAST486_LDT_SIGNATURE)
754 {
755 /* This is not an LDT descriptor */
756 Fast486ExceptionWithErrorCode(State, FAST486_EXCEPTION_TS, NewTss.Ldtr);
757 return FALSE;
758 }
759
760 if (!GdtEntry.Present)
761 {
762 Fast486ExceptionWithErrorCode(State, FAST486_EXCEPTION_TS, NewTss.Ldtr);
763 return FALSE;
764 }
765
766 /* Update the LDTR */
767 State->Ldtr.Selector = NewTss.Ldtr;
768 State->Ldtr.Base = GdtEntry.Base | (GdtEntry.BaseMid << 16) | (GdtEntry.BaseHigh << 24);
769 State->Ldtr.Limit = GdtEntry.Limit | (GdtEntry.LimitHigh << 16);
770
771 if (GdtEntry.Granularity)
772 {
773 State->Ldtr.Limit <<= 12;
774 State->Ldtr.Limit |= 0x00000FFF;
775 }
776 }
777 else
778 {
779 /* The LDT of this task is empty */
780 RtlZeroMemory(&State->Ldtr, sizeof(State->Ldtr));
781 }
782
783 /* Load the new segments */
784 if (!Fast486LoadSegmentInternal(State,
785 FAST486_REG_CS,
786 NewTss.Cs,
787 FAST486_EXCEPTION_TS))
788 {
789 return FALSE;
790 }
791
792 if (!Fast486LoadSegmentInternal(State,
793 FAST486_REG_SS,
794 NewTss.Ss,
795 FAST486_EXCEPTION_TS))
796 {
797 return FALSE;
798 }
799
800 if (!Fast486LoadSegmentInternal(State,
801 FAST486_REG_ES,
802 NewTss.Es,
803 FAST486_EXCEPTION_TS))
804 {
805 return FALSE;
806 }
807
808 if (!Fast486LoadSegmentInternal(State,
809 FAST486_REG_DS,
810 NewTss.Ds,
811 FAST486_EXCEPTION_TS))
812 {
813 return FALSE;
814 }
815
816 if (!Fast486LoadSegmentInternal(State,
817 FAST486_REG_FS,
818 NewTss.Fs,
819 FAST486_EXCEPTION_TS))
820 {
821 return FALSE;
822 }
823
824 if (!Fast486LoadSegmentInternal(State,
825 FAST486_REG_GS,
826 NewTss.Gs,
827 FAST486_EXCEPTION_TS))
828 {
829 return FALSE;
830 }
831
832 return TRUE;
833 }
834
835 /* EOF */