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