Fix a comment...
[reactos.git] / lib / fast486 / common.c
1 /*
2 * Fast486 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 #include <windef.h>
25
26 // #define NDEBUG
27 #include <debug.h>
28
29 #include <fast486.h>
30 #include "common.h"
31
32 /* PRIVATE FUNCTIONS **********************************************************/
33
34 static inline
35 ULONG
36 Fast486GetPageTableEntry(PFAST486_STATE State,
37 ULONG VirtualAddress)
38 {
39 // TODO: NOT IMPLEMENTED
40 UNIMPLEMENTED;
41
42 return 0;
43 }
44
45 /* PUBLIC FUNCTIONS ***********************************************************/
46
47 BOOLEAN
48 Fast486ReadMemory(PFAST486_STATE State,
49 FAST486_SEG_REGS SegmentReg,
50 ULONG Offset,
51 BOOLEAN InstFetch,
52 PVOID Buffer,
53 ULONG Size)
54 {
55 ULONG LinearAddress;
56 PFAST486_SEG_REG CachedDescriptor;
57 INT Cpl = Fast486GetCurrentPrivLevel(State);
58
59 ASSERT(SegmentReg < FAST486_NUM_SEG_REGS);
60
61 /* Get the cached descriptor */
62 CachedDescriptor = &State->SegmentRegs[SegmentReg];
63
64 if ((Offset + Size - 1) > CachedDescriptor->Limit)
65 {
66 /* Read beyond limit */
67 Fast486Exception(State, FAST486_EXCEPTION_GP);
68 return FALSE;
69 }
70
71 /* Check for protected mode */
72 if (State->ControlRegisters[0] & FAST486_CR0_PE)
73 {
74 /* Privilege checks */
75
76 if (!CachedDescriptor->Present)
77 {
78 Fast486Exception(State, FAST486_EXCEPTION_NP);
79 return FALSE;
80 }
81
82 if (GET_SEGMENT_RPL(CachedDescriptor->Selector) > 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 /* Check if paging is enabled */
112 if (State->ControlRegisters[FAST486_REG_CR0] & FAST486_CR0_PG)
113 {
114 ULONG Page;
115 FAST486_PAGE_TABLE TableEntry;
116
117 for (Page = PAGE_ALIGN(LinearAddress);
118 Page <= PAGE_ALIGN(LinearAddress + Size - 1);
119 Page += PAGE_SIZE)
120 {
121 ULONG PageOffset = 0, PageLength = PAGE_SIZE;
122
123 /* Get the table entry */
124 TableEntry.Value = Fast486GetPageTableEntry(State, Page);
125
126 if (!TableEntry.Present || (!TableEntry.Usermode && (Cpl > 0)))
127 {
128 /* Exception */
129 Fast486ExceptionWithErrorCode(State,
130 FAST486_EXCEPTION_PF,
131 TableEntry.Value & 0x07);
132 return FALSE;
133 }
134
135 /* Check if this is the first page */
136 if (Page == PAGE_ALIGN(LinearAddress))
137 {
138 /* Start copying from the offset from the beginning of the page */
139 PageOffset = PAGE_OFFSET(LinearAddress);
140 }
141
142 /* Check if this is the last page */
143 if (Page == PAGE_ALIGN(LinearAddress + Size - 1))
144 {
145 /* Copy only a part of the page */
146 PageLength = PAGE_OFFSET(LinearAddress + Size);
147 }
148
149 /* Read the memory */
150 State->MemReadCallback(State,
151 (TableEntry.Address << 12) | PageOffset,
152 Buffer,
153 PageLength);
154 }
155 }
156 else
157 {
158 /* Read the memory */
159 State->MemReadCallback(State, LinearAddress, Buffer, Size);
160 }
161
162 return TRUE;
163 }
164
165 BOOLEAN
166 Fast486WriteMemory(PFAST486_STATE State,
167 FAST486_SEG_REGS SegmentReg,
168 ULONG Offset,
169 PVOID Buffer,
170 ULONG Size)
171 {
172 ULONG LinearAddress;
173 PFAST486_SEG_REG CachedDescriptor;
174 INT Cpl = Fast486GetCurrentPrivLevel(State);
175
176 ASSERT(SegmentReg < FAST486_NUM_SEG_REGS);
177
178 /* Get the cached descriptor */
179 CachedDescriptor = &State->SegmentRegs[SegmentReg];
180
181 if ((Offset + Size - 1) > CachedDescriptor->Limit)
182 {
183 /* Write beyond limit */
184 Fast486Exception(State, FAST486_EXCEPTION_GP);
185
186 return FALSE;
187 }
188
189 /* Check for protected mode */
190 if (State->ControlRegisters[0] & FAST486_CR0_PE)
191 {
192 /* Privilege checks */
193
194 if (!CachedDescriptor->Present)
195 {
196 Fast486Exception(State, FAST486_EXCEPTION_NP);
197 return FALSE;
198 }
199
200 if (GET_SEGMENT_RPL(CachedDescriptor->Selector) > CachedDescriptor->Dpl)
201 {
202 Fast486Exception(State, FAST486_EXCEPTION_GP);
203 return FALSE;
204 }
205
206 if (CachedDescriptor->Executable)
207 {
208 /* Code segment not writable */
209 Fast486Exception(State, FAST486_EXCEPTION_GP);
210 return FALSE;
211 }
212 else if (!CachedDescriptor->ReadWrite)
213 {
214 /* Data segment not writeable */
215 Fast486Exception(State, FAST486_EXCEPTION_GP);
216 return FALSE;
217 }
218 }
219
220 /* Find the linear address */
221 LinearAddress = CachedDescriptor->Base + Offset;
222
223 /* Check if paging is enabled */
224 if (State->ControlRegisters[FAST486_REG_CR0] & FAST486_CR0_PG)
225 {
226 ULONG Page;
227 FAST486_PAGE_TABLE TableEntry;
228
229 for (Page = PAGE_ALIGN(LinearAddress);
230 Page <= PAGE_ALIGN(LinearAddress + Size - 1);
231 Page += PAGE_SIZE)
232 {
233 ULONG PageOffset = 0, PageLength = PAGE_SIZE;
234
235 /* Get the table entry */
236 TableEntry.Value = Fast486GetPageTableEntry(State, Page);
237
238 if ((!TableEntry.Present || (!TableEntry.Usermode && (Cpl > 0)))
239 || ((State->ControlRegisters[FAST486_REG_CR0] & FAST486_CR0_WP)
240 && !TableEntry.Writeable))
241 {
242 /* Exception */
243 Fast486ExceptionWithErrorCode(State,
244 FAST486_EXCEPTION_PF,
245 TableEntry.Value & 0x07);
246 return FALSE;
247 }
248
249 /* Check if this is the first page */
250 if (Page == PAGE_ALIGN(LinearAddress))
251 {
252 /* Start copying from the offset from the beginning of the page */
253 PageOffset = PAGE_OFFSET(LinearAddress);
254 }
255
256 /* Check if this is the last page */
257 if (Page == PAGE_ALIGN(LinearAddress + Size - 1))
258 {
259 /* Copy only a part of the page */
260 PageLength = PAGE_OFFSET(LinearAddress + Size);
261 }
262
263 /* Write the memory */
264 State->MemWriteCallback(State,
265 (TableEntry.Address << 12) | PageOffset,
266 Buffer,
267 PageLength);
268 }
269 }
270 else
271 {
272 /* Write the memory */
273 State->MemWriteCallback(State, LinearAddress, Buffer, Size);
274 }
275
276 return TRUE;
277 }
278
279 BOOLEAN
280 Fast486InterruptInternal(PFAST486_STATE State,
281 USHORT SegmentSelector,
282 ULONG Offset,
283 BOOLEAN InterruptGate)
284 {
285 /* Check for protected mode */
286 if (State->ControlRegisters[FAST486_REG_CR0] & FAST486_CR0_PE)
287 {
288 FAST486_TSS Tss;
289 USHORT OldSs = State->SegmentRegs[FAST486_REG_SS].Selector;
290 ULONG OldEsp = State->GeneralRegs[FAST486_REG_ESP].Long;
291
292 /* Check if the interrupt handler is more privileged */
293 if (Fast486GetCurrentPrivLevel(State) > GET_SEGMENT_RPL(SegmentSelector))
294 {
295 /* Read the TSS */
296 // FIXME: This code is only correct when paging is disabled!!!
297 State->MemReadCallback(State,
298 State->Tss.Address,
299 &Tss,
300 sizeof(Tss));
301
302 /* Check the new (higher) privilege level */
303 switch (GET_SEGMENT_RPL(SegmentSelector))
304 {
305 case 0:
306 {
307 if (!Fast486LoadSegment(State, FAST486_REG_SS, Tss.Ss0))
308 {
309 /* Exception occurred */
310 return FALSE;
311 }
312 State->GeneralRegs[FAST486_REG_ESP].Long = Tss.Esp0;
313
314 break;
315 }
316
317 case 1:
318 {
319 if (!Fast486LoadSegment(State, FAST486_REG_SS, Tss.Ss1))
320 {
321 /* Exception occurred */
322 return FALSE;
323 }
324 State->GeneralRegs[FAST486_REG_ESP].Long = Tss.Esp1;
325
326 break;
327 }
328
329 case 2:
330 {
331 if (!Fast486LoadSegment(State, FAST486_REG_SS, Tss.Ss2))
332 {
333 /* Exception occurred */
334 return FALSE;
335 }
336 State->GeneralRegs[FAST486_REG_ESP].Long = Tss.Esp2;
337
338 break;
339 }
340
341 default:
342 {
343 /* Should never reach here! */
344 ASSERT(FALSE);
345 }
346 }
347
348 /* Push SS selector */
349 if (!Fast486StackPush(State, OldSs)) return FALSE;
350
351 /* Push stack pointer */
352 if (!Fast486StackPush(State, OldEsp)) return FALSE;
353 }
354 }
355
356 /* Push EFLAGS */
357 if (!Fast486StackPush(State, State->Flags.Long)) return FALSE;
358
359 /* Push CS selector */
360 if (!Fast486StackPush(State, State->SegmentRegs[FAST486_REG_CS].Selector)) return FALSE;
361
362 /* Push the instruction pointer */
363 if (!Fast486StackPush(State, State->InstPtr.Long)) return FALSE;
364
365 if (InterruptGate)
366 {
367 /* Disable interrupts after a jump to an interrupt gate handler */
368 State->Flags.If = FALSE;
369 }
370
371 /* Load new CS */
372 if (!Fast486LoadSegment(State, FAST486_REG_CS, SegmentSelector))
373 {
374 /* An exception occurred during the jump */
375 return FALSE;
376 }
377
378 if (State->SegmentRegs[FAST486_REG_CS].Size)
379 {
380 /* 32-bit code segment, use EIP */
381 State->InstPtr.Long = Offset;
382 }
383 else
384 {
385 /* 16-bit code segment, use IP */
386 State->InstPtr.LowWord = LOWORD(Offset);
387 }
388
389 return TRUE;
390 }
391
392 VOID
393 FASTCALL
394 Fast486ExceptionWithErrorCode(PFAST486_STATE State,
395 FAST486_EXCEPTIONS ExceptionCode,
396 ULONG ErrorCode)
397 {
398 FAST486_IDT_ENTRY IdtEntry;
399
400 /* Increment the exception count */
401 State->ExceptionCount++;
402
403 /* Check if the exception occurred more than once */
404 if (State->ExceptionCount > 1)
405 {
406 /* Then this is a double fault */
407 ExceptionCode = FAST486_EXCEPTION_DF;
408 }
409
410 /* Check if this is a triple fault */
411 if (State->ExceptionCount == 3)
412 {
413 /* Reset the CPU */
414 Fast486Reset(State);
415 return;
416 }
417
418 /* Restore the IP to the saved IP */
419 State->InstPtr = State->SavedInstPtr;
420
421 if (!Fast486GetIntVector(State, ExceptionCode, &IdtEntry))
422 {
423 /*
424 * If this function failed, that means Fast486Exception
425 * was called again, so just return in this case.
426 */
427 return;
428 }
429
430 /* Perform the interrupt */
431 if (!Fast486InterruptInternal(State,
432 IdtEntry.Selector,
433 MAKELONG(IdtEntry.Offset, IdtEntry.OffsetHigh),
434 IdtEntry.Type))
435 {
436 /*
437 * If this function failed, that means Fast486Exception
438 * was called again, so just return in this case.
439 */
440 return;
441 }
442
443 if (EXCEPTION_HAS_ERROR_CODE(ExceptionCode)
444 && (State->ControlRegisters[FAST486_REG_CR0] & FAST486_CR0_PE))
445 {
446 /* Push the error code */
447 Fast486StackPush(State, ErrorCode);
448 }
449 }
450
451 /* EOF */