Fix a compilation error.
[reactos.git] / lib / soft386 / common.c
1 /*
2 * COPYRIGHT: GPL - See COPYING in the top level directory
3 * PROJECT: 386/486 CPU Emulation Library
4 * FILE: common.c
5 * PURPOSE: Common functions used internally by Soft386.
6 * PROGRAMMERS: Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
7 */
8
9 /* INCLUDES *******************************************************************/
10
11 // #define WIN32_NO_STATUS
12 // #define _INC_WINDOWS
13 #include <windef.h>
14
15 // #define NDEBUG
16 #include <debug.h>
17
18 #include <soft386.h>
19 #include "common.h"
20
21 /* PRIVATE FUNCTIONS **********************************************************/
22
23 static /* FORCEINLINE */
24 ULONG
25 Soft386GetPageTableEntry(PSOFT386_STATE State,
26 ULONG VirtualAddress)
27 {
28 // TODO: NOT IMPLEMENTED
29 UNIMPLEMENTED;
30
31 return 0;
32 }
33
34 /* PUBLIC FUNCTIONS ***********************************************************/
35
36 BOOLEAN
37 Soft386ReadMemory(PSOFT386_STATE State,
38 SOFT386_SEG_REGS SegmentReg,
39 ULONG Offset,
40 BOOLEAN InstFetch,
41 PVOID Buffer,
42 ULONG Size)
43 {
44 ULONG LinearAddress;
45 PSOFT386_SEG_REG CachedDescriptor;
46 INT Cpl = Soft386GetCurrentPrivLevel(State);
47
48 ASSERT(SegmentReg < SOFT386_NUM_SEG_REGS);
49
50 /* Get the cached descriptor */
51 CachedDescriptor = &State->SegmentRegs[SegmentReg];
52
53 if ((Offset + Size) >= CachedDescriptor->Limit)
54 {
55 /* Read beyond limit */
56 Soft386Exception(State, SOFT386_EXCEPTION_GP);
57
58 return FALSE;
59 }
60
61 /* Check for protected mode */
62 if (State->ControlRegisters[0] & SOFT386_CR0_PE)
63 {
64 /* Privilege checks */
65
66 if (!CachedDescriptor->Present)
67 {
68 Soft386Exception(State, SOFT386_EXCEPTION_NP);
69 return FALSE;
70 }
71
72 if (GET_SEGMENT_RPL(CachedDescriptor->Selector) > CachedDescriptor->Dpl)
73 {
74 Soft386Exception(State, SOFT386_EXCEPTION_GP);
75 return FALSE;
76 }
77
78 if (InstFetch)
79 {
80 if (!CachedDescriptor->Executable)
81 {
82 /* Data segment not executable */
83
84 Soft386Exception(State, SOFT386_EXCEPTION_GP);
85 return FALSE;
86 }
87 }
88 else
89 {
90 if (CachedDescriptor->Executable && (!CachedDescriptor->ReadWrite))
91 {
92 /* Code segment not readable */
93
94 Soft386Exception(State, SOFT386_EXCEPTION_GP);
95 return FALSE;
96 }
97 }
98 }
99
100 /* Find the linear address */
101 LinearAddress = CachedDescriptor->Base + Offset;
102
103 /* Check if paging is enabled */
104 if (State->ControlRegisters[SOFT386_REG_CR0] & SOFT386_CR0_PG)
105 {
106 ULONG Page;
107 SOFT386_PAGE_TABLE TableEntry;
108
109 for (Page = PAGE_ALIGN(LinearAddress);
110 Page <= PAGE_ALIGN(LinearAddress + Size - 1);
111 Page += PAGE_SIZE)
112 {
113 ULONG PageOffset = 0, PageLength = PAGE_SIZE;
114
115 /* Get the table entry */
116 TableEntry.Value = Soft386GetPageTableEntry(State, Page);
117
118 if (!TableEntry.Present || (!TableEntry.Usermode && (Cpl > 0)))
119 {
120 /* Exception */
121 Soft386ExceptionWithErrorCode(State,
122 SOFT386_EXCEPTION_PF,
123 TableEntry.Value & 0x07);
124 return FALSE;
125 }
126
127 /* Check if this is the first page */
128 if (Page == PAGE_ALIGN(LinearAddress))
129 {
130 /* Start copying from the offset from the beginning of the page */
131 PageOffset = PAGE_OFFSET(LinearAddress);
132 }
133
134 /* Check if this is the last page */
135 if (Page == PAGE_ALIGN(LinearAddress + Size - 1))
136 {
137 /* Copy only a part of the page */
138 PageLength = PAGE_OFFSET(LinearAddress + Size);
139 }
140
141 /* Did the host provide a memory hook? */
142 if (State->MemReadCallback)
143 {
144 /* Yes, call the host */
145 State->MemReadCallback(State,
146 (TableEntry.Address << 12) | PageOffset,
147 Buffer,
148 PageLength);
149 }
150 else
151 {
152 /* Read the memory directly */
153 RtlMoveMemory(Buffer,
154 (PVOID)((TableEntry.Address << 12) | PageOffset),
155 PageLength);
156 }
157 }
158 }
159 else
160 {
161 /* Did the host provide a memory hook? */
162 if (State->MemReadCallback)
163 {
164 /* Yes, call the host */
165 State->MemReadCallback(State, LinearAddress, Buffer, Size);
166 }
167 else
168 {
169 /* Read the memory directly */
170 RtlMoveMemory(Buffer, (PVOID)LinearAddress, Size);
171 }
172 }
173
174 return TRUE;
175 }
176
177 BOOLEAN
178 Soft386WriteMemory(PSOFT386_STATE State,
179 SOFT386_SEG_REGS SegmentReg,
180 ULONG Offset,
181 PVOID Buffer,
182 ULONG Size)
183 {
184 ULONG LinearAddress;
185 PSOFT386_SEG_REG CachedDescriptor;
186 INT Cpl = Soft386GetCurrentPrivLevel(State);
187
188 ASSERT(SegmentReg < SOFT386_NUM_SEG_REGS);
189
190 /* Get the cached descriptor */
191 CachedDescriptor = &State->SegmentRegs[SegmentReg];
192
193 if ((Offset + Size) >= CachedDescriptor->Limit)
194 {
195 /* Write beyond limit */
196 Soft386Exception(State, SOFT386_EXCEPTION_GP);
197
198 return FALSE;
199 }
200
201 /* Check for protected mode */
202 if (State->ControlRegisters[0] & SOFT386_CR0_PE)
203 {
204 /* Privilege checks */
205
206 if (!CachedDescriptor->Present)
207 {
208 Soft386Exception(State, SOFT386_EXCEPTION_NP);
209 return FALSE;
210 }
211
212 if (GET_SEGMENT_RPL(CachedDescriptor->Selector) > CachedDescriptor->Dpl)
213 {
214 Soft386Exception(State, SOFT386_EXCEPTION_GP);
215 return FALSE;
216 }
217
218 if (CachedDescriptor->Executable)
219 {
220 /* Code segment not writable */
221
222 Soft386Exception(State, SOFT386_EXCEPTION_GP);
223 return FALSE;
224 }
225 else if (!CachedDescriptor->ReadWrite)
226 {
227 /* Data segment not writeable */
228
229 Soft386Exception(State, SOFT386_EXCEPTION_GP);
230 return FALSE;
231 }
232 }
233
234 /* Find the linear address */
235 LinearAddress = CachedDescriptor->Base + Offset;
236
237 /* Check if paging is enabled */
238 if (State->ControlRegisters[SOFT386_REG_CR0] & SOFT386_CR0_PG)
239 {
240 ULONG Page;
241 SOFT386_PAGE_TABLE TableEntry;
242
243 for (Page = PAGE_ALIGN(LinearAddress);
244 Page <= PAGE_ALIGN(LinearAddress + Size - 1);
245 Page += PAGE_SIZE)
246 {
247 ULONG PageOffset = 0, PageLength = PAGE_SIZE;
248
249 /* Get the table entry */
250 TableEntry.Value = Soft386GetPageTableEntry(State, Page);
251
252 if ((!TableEntry.Present || (!TableEntry.Usermode && (Cpl > 0)))
253 || ((State->ControlRegisters[SOFT386_REG_CR0] & SOFT386_CR0_WP)
254 && !TableEntry.Writeable))
255 {
256 /* Exception */
257 Soft386ExceptionWithErrorCode(State,
258 SOFT386_EXCEPTION_PF,
259 TableEntry.Value & 0x07);
260 return FALSE;
261 }
262
263 /* Check if this is the first page */
264 if (Page == PAGE_ALIGN(LinearAddress))
265 {
266 /* Start copying from the offset from the beginning of the page */
267 PageOffset = PAGE_OFFSET(LinearAddress);
268 }
269
270 /* Check if this is the last page */
271 if (Page == PAGE_ALIGN(LinearAddress + Size - 1))
272 {
273 /* Copy only a part of the page */
274 PageLength = PAGE_OFFSET(LinearAddress + Size);
275 }
276
277 /* Did the host provide a memory hook? */
278 if (State->MemWriteCallback)
279 {
280 /* Yes, call the host */
281 State->MemWriteCallback(State,
282 (TableEntry.Address << 12) | PageOffset,
283 Buffer,
284 PageLength);
285 }
286 else
287 {
288 /* Read the memory directly */
289 RtlMoveMemory((PVOID)((TableEntry.Address << 12) | PageOffset),
290 Buffer,
291 PageLength);
292 }
293 }
294 }
295 else
296 {
297 /* Did the host provide a memory hook? */
298 if (State->MemWriteCallback)
299 {
300 /* Yes, call the host */
301 State->MemWriteCallback(State, LinearAddress, Buffer, Size);
302 }
303 else
304 {
305 /* Write the memory directly */
306 RtlMoveMemory((PVOID)LinearAddress, Buffer, Size);
307 }
308 }
309
310 return TRUE;
311 }
312
313 BOOLEAN
314 Soft386InterruptInternal(PSOFT386_STATE State,
315 USHORT SegmentSelector,
316 ULONG Offset,
317 BOOLEAN InterruptGate)
318 {
319 /* Check for protected mode */
320 if (State->ControlRegisters[SOFT386_REG_CR0] & SOFT386_CR0_PE)
321 {
322 SOFT386_TSS Tss;
323 USHORT OldSs = State->SegmentRegs[SOFT386_REG_SS].Selector;
324 ULONG OldEsp = State->GeneralRegs[SOFT386_REG_ESP].Long;
325
326 /* Check if the interrupt handler is more privileged */
327 if (Soft386GetCurrentPrivLevel(State) > GET_SEGMENT_RPL(SegmentSelector))
328 {
329 /* Read the TSS */
330 // FIXME: This code is only correct when paging is disabled!!!
331 if (State->MemReadCallback)
332 {
333 State->MemReadCallback(State,
334 State->Tss.Address,
335 &Tss,
336 sizeof(Tss));
337 }
338 else
339 {
340 RtlMoveMemory(&Tss, (PVOID)State->Tss.Address, sizeof(Tss));
341 }
342
343 /* Check the new (higher) privilege level */
344 switch (GET_SEGMENT_RPL(SegmentSelector))
345 {
346 case 0:
347 {
348 if (!Soft386LoadSegment(State, SOFT386_REG_SS, Tss.Ss0))
349 {
350 /* Exception occurred */
351 return FALSE;
352 }
353 State->GeneralRegs[SOFT386_REG_ESP].Long = Tss.Esp0;
354
355 break;
356 }
357
358 case 1:
359 {
360 if (!Soft386LoadSegment(State, SOFT386_REG_SS, Tss.Ss1))
361 {
362 /* Exception occurred */
363 return FALSE;
364 }
365 State->GeneralRegs[SOFT386_REG_ESP].Long = Tss.Esp1;
366
367 break;
368 }
369
370 case 2:
371 {
372 if (!Soft386LoadSegment(State, SOFT386_REG_SS, Tss.Ss2))
373 {
374 /* Exception occurred */
375 return FALSE;
376 }
377 State->GeneralRegs[SOFT386_REG_ESP].Long = Tss.Esp2;
378
379 break;
380 }
381
382 default:
383 {
384 /* Should never reach here! */
385 ASSERT(FALSE);
386 }
387 }
388
389 /* Push SS selector */
390 if (!Soft386StackPush(State, OldSs)) return FALSE;
391
392 /* Push stack pointer */
393 if (!Soft386StackPush(State, OldEsp)) return FALSE;
394 }
395 }
396
397 /* Push EFLAGS */
398 if (!Soft386StackPush(State, State->Flags.Long)) return FALSE;
399
400 /* Push CS selector */
401 if (!Soft386StackPush(State, State->SegmentRegs[SOFT386_REG_CS].Selector)) return FALSE;
402
403 /* Push the instruction pointer */
404 if (!Soft386StackPush(State, State->InstPtr.Long)) return FALSE;
405
406 if (InterruptGate)
407 {
408 /* Disable interrupts after a jump to an interrupt gate handler */
409 State->Flags.If = FALSE;
410 }
411
412 /* Load new CS */
413 if (!Soft386LoadSegment(State, SOFT386_REG_CS, SegmentSelector))
414 {
415 /* An exception occurred during the jump */
416 return FALSE;
417 }
418
419 if (State->SegmentRegs[SOFT386_REG_CS].Size)
420 {
421 /* 32-bit code segment, use EIP */
422 State->InstPtr.Long = Offset;
423 }
424 else
425 {
426 /* 16-bit code segment, use IP */
427 State->InstPtr.LowWord = LOWORD(Offset);
428 }
429
430 return TRUE;
431 }
432
433 VOID
434 FASTCALL
435 Soft386ExceptionWithErrorCode(PSOFT386_STATE State,
436 INT ExceptionCode,
437 ULONG ErrorCode)
438 {
439 SOFT386_IDT_ENTRY IdtEntry;
440
441 /* Increment the exception count */
442 State->ExceptionCount++;
443
444 /* Check if the exception occurred more than once */
445 if (State->ExceptionCount > 1)
446 {
447 /* Then this is a double fault */
448 ExceptionCode = SOFT386_EXCEPTION_DF;
449 }
450
451 /* Check if this is a triple fault */
452 if (State->ExceptionCount == 3)
453 {
454 /* Reset the CPU */
455 Soft386Reset(State);
456 return;
457 }
458
459 if (!Soft386GetIntVector(State, ExceptionCode, &IdtEntry))
460 {
461 /*
462 * If this function failed, that means Soft386Exception
463 * was called again, so just return in this case.
464 */
465 return;
466 }
467
468 /* Perform the interrupt */
469 if (!Soft386InterruptInternal(State,
470 IdtEntry.Selector,
471 MAKELONG(IdtEntry.Offset, IdtEntry.OffsetHigh),
472 IdtEntry.Type))
473 {
474 /*
475 * If this function failed, that means Soft386Exception
476 * was called again, so just return in this case.
477 */
478 return;
479 }
480
481 if (EXCEPTION_HAS_ERROR_CODE(ExceptionCode))
482 {
483 /* Push the error code */
484 Soft386StackPush(State, ErrorCode);
485 }
486 }
487
488 /* EOF */