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