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