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