[FAST486]
[reactos.git] / reactos / lib / fast486 / common.c
1 /*
2 * Fast486 386/486 CPU Emulation Library
3 * common.c
4 *
5 * Copyright (C) 2014 Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 */
21
22 /* INCLUDES *******************************************************************/
23
24 #include <windef.h>
25
26 // #define NDEBUG
27 #include <debug.h>
28
29 #include <fast486.h>
30 #include "common.h"
31
32 /* PUBLIC FUNCTIONS ***********************************************************/
33
34 BOOLEAN
35 Fast486ReadMemory(PFAST486_STATE State,
36 FAST486_SEG_REGS SegmentReg,
37 ULONG Offset,
38 BOOLEAN InstFetch,
39 PVOID Buffer,
40 ULONG Size)
41 {
42 ULONG LinearAddress;
43 PFAST486_SEG_REG CachedDescriptor;
44
45 ASSERT(SegmentReg < FAST486_NUM_SEG_REGS);
46
47 /* Get the cached descriptor */
48 CachedDescriptor = &State->SegmentRegs[SegmentReg];
49
50 if ((Offset + Size - 1) > CachedDescriptor->Limit)
51 {
52 /* Read beyond limit */
53 Fast486Exception(State, FAST486_EXCEPTION_GP);
54 return FALSE;
55 }
56
57 /* Check for protected mode */
58 if (State->ControlRegisters[FAST486_REG_CR0] & FAST486_CR0_PE)
59 {
60 /* Privilege checks */
61
62 if (!CachedDescriptor->Present)
63 {
64 Fast486Exception(State, FAST486_EXCEPTION_NP);
65 return FALSE;
66 }
67
68 if ((!InstFetch && (CachedDescriptor->Rpl > CachedDescriptor->Dpl))
69 || (Fast486GetCurrentPrivLevel(State) > CachedDescriptor->Dpl))
70 {
71 Fast486Exception(State, FAST486_EXCEPTION_GP);
72 return FALSE;
73 }
74
75 if (InstFetch)
76 {
77 if (!CachedDescriptor->Executable)
78 {
79 /* Data segment not executable */
80 Fast486Exception(State, FAST486_EXCEPTION_GP);
81 return FALSE;
82 }
83 }
84 else
85 {
86 if (CachedDescriptor->Executable && (!CachedDescriptor->ReadWrite))
87 {
88 /* Code segment not readable */
89 Fast486Exception(State, FAST486_EXCEPTION_GP);
90 return FALSE;
91 }
92 }
93 }
94
95 /* Find the linear address */
96 LinearAddress = CachedDescriptor->Base + Offset;
97
98 #ifndef FAST486_NO_PREFETCH
99 if (InstFetch && ((Offset + FAST486_CACHE_SIZE - 1) <= CachedDescriptor->Limit))
100 {
101 State->PrefetchAddress = LinearAddress;
102
103 if ((State->ControlRegisters[FAST486_REG_CR0] & FAST486_CR0_PG)
104 && (PAGE_OFFSET(State->PrefetchAddress) > (FAST486_PAGE_SIZE - FAST486_CACHE_SIZE)))
105 {
106 /* We mustn't prefetch across a page boundary */
107 State->PrefetchAddress = PAGE_ALIGN(State->PrefetchAddress)
108 | (FAST486_PAGE_SIZE - FAST486_CACHE_SIZE);
109 }
110
111 /* Prefetch */
112 if (Fast486ReadLinearMemory(State,
113 State->PrefetchAddress,
114 State->PrefetchCache,
115 FAST486_CACHE_SIZE))
116 {
117 State->PrefetchValid = TRUE;
118
119 RtlMoveMemory(Buffer,
120 &State->PrefetchCache[LinearAddress - State->PrefetchAddress],
121 Size);
122 return TRUE;
123 }
124 else
125 {
126 State->PrefetchValid = FALSE;
127 return FALSE;
128 }
129 }
130 else
131 #endif
132 {
133 /* Read from the linear address */
134 return Fast486ReadLinearMemory(State, LinearAddress, Buffer, Size);
135 }
136 }
137
138 BOOLEAN
139 Fast486WriteMemory(PFAST486_STATE State,
140 FAST486_SEG_REGS SegmentReg,
141 ULONG Offset,
142 PVOID Buffer,
143 ULONG Size)
144 {
145 ULONG LinearAddress;
146 PFAST486_SEG_REG CachedDescriptor;
147
148 ASSERT(SegmentReg < FAST486_NUM_SEG_REGS);
149
150 /* Get the cached descriptor */
151 CachedDescriptor = &State->SegmentRegs[SegmentReg];
152
153 if ((Offset + Size - 1) > CachedDescriptor->Limit)
154 {
155 /* Write beyond limit */
156 Fast486Exception(State, FAST486_EXCEPTION_GP);
157 return FALSE;
158 }
159
160 /* Check for protected mode */
161 if (State->ControlRegisters[FAST486_REG_CR0] & FAST486_CR0_PE)
162 {
163 /* Privilege checks */
164
165 if (!CachedDescriptor->Present)
166 {
167 Fast486Exception(State, FAST486_EXCEPTION_NP);
168 return FALSE;
169 }
170
171 if ((CachedDescriptor->Rpl > CachedDescriptor->Dpl)
172 || (Fast486GetCurrentPrivLevel(State) > CachedDescriptor->Dpl))
173 {
174 Fast486Exception(State, FAST486_EXCEPTION_GP);
175 return FALSE;
176 }
177
178 if (CachedDescriptor->Executable)
179 {
180 /* Code segment not writable */
181 Fast486Exception(State, FAST486_EXCEPTION_GP);
182 return FALSE;
183 }
184 else if (!CachedDescriptor->ReadWrite)
185 {
186 /* Data segment not writeable */
187 Fast486Exception(State, FAST486_EXCEPTION_GP);
188 return FALSE;
189 }
190 }
191
192 /* Find the linear address */
193 LinearAddress = CachedDescriptor->Base + Offset;
194
195 #ifndef FAST486_NO_PREFETCH
196 if (State->PrefetchValid
197 && (LinearAddress >= State->PrefetchAddress)
198 && ((LinearAddress + Size) <= (State->PrefetchAddress + FAST486_CACHE_SIZE)))
199 {
200 /* Update the prefetch */
201 RtlMoveMemory(&State->PrefetchCache[LinearAddress - State->PrefetchAddress],
202 Buffer,
203 min(Size, FAST486_CACHE_SIZE + State->PrefetchAddress - LinearAddress));
204 }
205 #endif
206
207 /* Write to the linear address */
208 return Fast486WriteLinearMemory(State, LinearAddress, Buffer, Size);
209 }
210
211 static inline BOOLEAN
212 FASTCALL
213 Fast486GetIntVector(PFAST486_STATE State,
214 UCHAR Number,
215 PFAST486_IDT_ENTRY IdtEntry)
216 {
217 /* Check for protected mode */
218 if (State->ControlRegisters[FAST486_REG_CR0] & FAST486_CR0_PE)
219 {
220 /* Read from the IDT */
221 if (!Fast486ReadLinearMemory(State,
222 State->Idtr.Address
223 + Number * sizeof(*IdtEntry),
224 IdtEntry,
225 sizeof(*IdtEntry)))
226 {
227 /* Exception occurred */
228 return FALSE;
229 }
230 }
231 else
232 {
233 /* Read from the real-mode IVT */
234 ULONG FarPointer;
235
236 /* Paging is always disabled in real mode */
237 State->MemReadCallback(State,
238 State->Idtr.Address
239 + Number * sizeof(FarPointer),
240 &FarPointer,
241 sizeof(FarPointer));
242
243 /* Fill a fake IDT entry */
244 IdtEntry->Offset = LOWORD(FarPointer);
245 IdtEntry->Selector = HIWORD(FarPointer);
246 IdtEntry->Zero = 0;
247 IdtEntry->Type = FAST486_IDT_INT_GATE;
248 IdtEntry->Storage = FALSE;
249 IdtEntry->Dpl = 0;
250 IdtEntry->Present = TRUE;
251 IdtEntry->OffsetHigh = 0;
252 }
253
254 return TRUE;
255 }
256
257 static inline BOOLEAN
258 FASTCALL
259 Fast486InterruptInternal(PFAST486_STATE State,
260 PFAST486_IDT_ENTRY IdtEntry)
261 {
262 USHORT SegmentSelector = IdtEntry->Selector;
263 ULONG Offset = MAKELONG(IdtEntry->Offset, IdtEntry->OffsetHigh);
264 ULONG GateType = IdtEntry->Type;
265 BOOLEAN GateSize = (GateType == FAST486_IDT_INT_GATE_32) ||
266 (GateType == FAST486_IDT_TRAP_GATE_32);
267
268 BOOLEAN Success = FALSE;
269 ULONG OldPrefixFlags = State->PrefixFlags;
270
271 /* Check for protected mode */
272 if (State->ControlRegisters[FAST486_REG_CR0] & FAST486_CR0_PE)
273 {
274 FAST486_TSS Tss;
275 USHORT OldSs = State->SegmentRegs[FAST486_REG_SS].Selector;
276 ULONG OldEsp = State->GeneralRegs[FAST486_REG_ESP].Long;
277
278 if (GateSize != (State->SegmentRegs[FAST486_REG_CS].Size))
279 {
280 /*
281 * The gate size doesn't match the current operand size, so toggle
282 * the OPSIZE flag.
283 */
284 State->PrefixFlags ^= FAST486_PREFIX_OPSIZE;
285 }
286
287 /* Check if the interrupt handler is more privileged */
288 if (Fast486GetCurrentPrivLevel(State) > GET_SEGMENT_RPL(SegmentSelector))
289 {
290 /* Read the TSS */
291 if (!Fast486ReadLinearMemory(State,
292 State->TaskReg.Base,
293 &Tss,
294 sizeof(Tss)))
295 {
296 /* Exception occurred */
297 goto Cleanup;
298 }
299
300 /* Check the new (higher) privilege level */
301 switch (GET_SEGMENT_RPL(SegmentSelector))
302 {
303 case 0:
304 {
305 if (!Fast486LoadSegment(State, FAST486_REG_SS, Tss.Ss0))
306 {
307 /* Exception occurred */
308 goto Cleanup;
309 }
310 State->GeneralRegs[FAST486_REG_ESP].Long = Tss.Esp0;
311
312 break;
313 }
314
315 case 1:
316 {
317 if (!Fast486LoadSegment(State, FAST486_REG_SS, Tss.Ss1))
318 {
319 /* Exception occurred */
320 goto Cleanup;
321 }
322 State->GeneralRegs[FAST486_REG_ESP].Long = Tss.Esp1;
323
324 break;
325 }
326
327 case 2:
328 {
329 if (!Fast486LoadSegment(State, FAST486_REG_SS, Tss.Ss2))
330 {
331 /* Exception occurred */
332 goto Cleanup;
333 }
334 State->GeneralRegs[FAST486_REG_ESP].Long = Tss.Esp2;
335
336 break;
337 }
338
339 default:
340 {
341 /* Should never reach here! */
342 ASSERT(FALSE);
343 }
344 }
345
346 /* Push SS selector */
347 if (!Fast486StackPush(State, OldSs)) goto Cleanup;
348
349 /* Push stack pointer */
350 if (!Fast486StackPush(State, OldEsp)) goto Cleanup;
351 }
352 }
353 else
354 {
355 if (State->SegmentRegs[FAST486_REG_CS].Size)
356 {
357 /* Set OPSIZE, because INT always pushes 16-bit values in real mode */
358 State->PrefixFlags |= FAST486_PREFIX_OPSIZE;
359 }
360 }
361
362 /* Push EFLAGS */
363 if (!Fast486StackPush(State, State->Flags.Long)) goto Cleanup;
364
365 /* Push CS selector */
366 if (!Fast486StackPush(State, State->SegmentRegs[FAST486_REG_CS].Selector)) goto Cleanup;
367
368 /* Push the instruction pointer */
369 if (!Fast486StackPush(State, State->InstPtr.Long)) goto Cleanup;
370
371 if ((GateType == FAST486_IDT_INT_GATE) || (GateType == FAST486_IDT_INT_GATE_32))
372 {
373 /* Disable interrupts after a jump to an interrupt gate handler */
374 State->Flags.If = FALSE;
375 }
376
377 /* Load new CS */
378 if (!Fast486LoadSegment(State, FAST486_REG_CS, SegmentSelector))
379 {
380 /* An exception occurred during the jump */
381 goto Cleanup;
382 }
383
384 if (GateSize)
385 {
386 /* 32-bit code segment, use EIP */
387 State->InstPtr.Long = Offset;
388 }
389 else
390 {
391 /* 16-bit code segment, use IP */
392 State->InstPtr.LowWord = LOWORD(Offset);
393 }
394
395 Success = TRUE;
396
397 Cleanup:
398 /* Restore the prefix flags */
399 State->PrefixFlags = OldPrefixFlags;
400
401 return Success;
402 }
403
404 BOOLEAN
405 FASTCALL
406 Fast486PerformInterrupt(PFAST486_STATE State,
407 UCHAR Number)
408 {
409 FAST486_IDT_ENTRY IdtEntry;
410
411 /* Get the interrupt vector */
412 if (!Fast486GetIntVector(State, Number, &IdtEntry))
413 {
414 /* Exception occurred */
415 return FALSE;
416 }
417
418 /* Perform the interrupt */
419 if (!Fast486InterruptInternal(State, &IdtEntry))
420 {
421 /* Exception occurred */
422 return FALSE;
423 }
424
425 return TRUE;
426 }
427
428 VOID
429 FASTCALL
430 Fast486ExceptionWithErrorCode(PFAST486_STATE State,
431 FAST486_EXCEPTIONS ExceptionCode,
432 ULONG ErrorCode)
433 {
434 /* Increment the exception count */
435 State->ExceptionCount++;
436
437 /* Check if the exception occurred more than once */
438 if (State->ExceptionCount > 1)
439 {
440 /* Then this is a double fault */
441 ExceptionCode = FAST486_EXCEPTION_DF;
442 }
443
444 /* Check if this is a triple fault */
445 if (State->ExceptionCount == 3)
446 {
447 DPRINT("Fast486ExceptionWithErrorCode(%04X:%08X) -- Triple fault\n",
448 State->SegmentRegs[FAST486_REG_CS].Selector,
449 State->InstPtr.Long);
450
451 /* Reset the CPU */
452 Fast486Reset(State);
453 return;
454 }
455
456 /* Restore the IP to the saved IP */
457 State->InstPtr = State->SavedInstPtr;
458
459 /* Perform the interrupt */
460 if (!Fast486PerformInterrupt(State, ExceptionCode))
461 {
462 /*
463 * If this function failed, that means Fast486Exception
464 * was called again, so just return in this case.
465 */
466 return;
467 }
468
469 if (EXCEPTION_HAS_ERROR_CODE(ExceptionCode)
470 && (State->ControlRegisters[FAST486_REG_CR0] & FAST486_CR0_PE))
471 {
472 /* Push the error code */
473 if (!Fast486StackPush(State, ErrorCode))
474 {
475 /*
476 * If this function failed, that means Fast486Exception
477 * was called again, so just return in this case.
478 */
479 return;
480 }
481 }
482
483 /* Reset the exception count */
484 State->ExceptionCount = 0;
485 }
486
487 /* EOF */