Sync with trunk r63192.
[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 /* 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[0] & 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 && (GET_SEGMENT_RPL(CachedDescriptor->Selector) > 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 /* Read from the linear address */
99 return Fast486ReadLinearMemory(State, LinearAddress, Buffer, Size);
100 }
101
102 BOOLEAN
103 Fast486WriteMemory(PFAST486_STATE State,
104 FAST486_SEG_REGS SegmentReg,
105 ULONG Offset,
106 PVOID Buffer,
107 ULONG Size)
108 {
109 ULONG LinearAddress;
110 PFAST486_SEG_REG CachedDescriptor;
111
112 ASSERT(SegmentReg < FAST486_NUM_SEG_REGS);
113
114 /* Get the cached descriptor */
115 CachedDescriptor = &State->SegmentRegs[SegmentReg];
116
117 if ((Offset + Size - 1) > CachedDescriptor->Limit)
118 {
119 /* Write beyond limit */
120 Fast486Exception(State, FAST486_EXCEPTION_GP);
121 return FALSE;
122 }
123
124 /* Check for protected mode */
125 if (State->ControlRegisters[0] & FAST486_CR0_PE)
126 {
127 /* Privilege checks */
128
129 if (!CachedDescriptor->Present)
130 {
131 Fast486Exception(State, FAST486_EXCEPTION_NP);
132 return FALSE;
133 }
134
135 if ((GET_SEGMENT_RPL(CachedDescriptor->Selector) > CachedDescriptor->Dpl)
136 || (Fast486GetCurrentPrivLevel(State) > CachedDescriptor->Dpl))
137 {
138 Fast486Exception(State, FAST486_EXCEPTION_GP);
139 return FALSE;
140 }
141
142 if (CachedDescriptor->Executable)
143 {
144 /* Code segment not writable */
145 Fast486Exception(State, FAST486_EXCEPTION_GP);
146 return FALSE;
147 }
148 else if (!CachedDescriptor->ReadWrite)
149 {
150 /* Data segment not writeable */
151 Fast486Exception(State, FAST486_EXCEPTION_GP);
152 return FALSE;
153 }
154 }
155
156 /* Find the linear address */
157 LinearAddress = CachedDescriptor->Base + Offset;
158
159 /* Write to the linear address */
160 return Fast486WriteLinearMemory(State, LinearAddress, Buffer, Size);
161 }
162
163 BOOLEAN
164 Fast486InterruptInternal(PFAST486_STATE State,
165 USHORT SegmentSelector,
166 ULONG Offset,
167 BOOLEAN InterruptGate)
168 {
169 /* Check for protected mode */
170 if (State->ControlRegisters[FAST486_REG_CR0] & FAST486_CR0_PE)
171 {
172 FAST486_TSS Tss;
173 USHORT OldSs = State->SegmentRegs[FAST486_REG_SS].Selector;
174 ULONG OldEsp = State->GeneralRegs[FAST486_REG_ESP].Long;
175
176 /* Check if the interrupt handler is more privileged */
177 if (Fast486GetCurrentPrivLevel(State) > GET_SEGMENT_RPL(SegmentSelector))
178 {
179 /* Read the TSS */
180 if (!Fast486ReadLinearMemory(State,
181 State->TaskReg.Base,
182 &Tss,
183 sizeof(Tss)))
184 {
185 /* Exception occurred */
186 return FALSE;
187 }
188
189 /* Check the new (higher) privilege level */
190 switch (GET_SEGMENT_RPL(SegmentSelector))
191 {
192 case 0:
193 {
194 if (!Fast486LoadSegment(State, FAST486_REG_SS, Tss.Ss0))
195 {
196 /* Exception occurred */
197 return FALSE;
198 }
199 State->GeneralRegs[FAST486_REG_ESP].Long = Tss.Esp0;
200
201 break;
202 }
203
204 case 1:
205 {
206 if (!Fast486LoadSegment(State, FAST486_REG_SS, Tss.Ss1))
207 {
208 /* Exception occurred */
209 return FALSE;
210 }
211 State->GeneralRegs[FAST486_REG_ESP].Long = Tss.Esp1;
212
213 break;
214 }
215
216 case 2:
217 {
218 if (!Fast486LoadSegment(State, FAST486_REG_SS, Tss.Ss2))
219 {
220 /* Exception occurred */
221 return FALSE;
222 }
223 State->GeneralRegs[FAST486_REG_ESP].Long = Tss.Esp2;
224
225 break;
226 }
227
228 default:
229 {
230 /* Should never reach here! */
231 ASSERT(FALSE);
232 }
233 }
234
235 /* Push SS selector */
236 if (!Fast486StackPush(State, OldSs)) return FALSE;
237
238 /* Push stack pointer */
239 if (!Fast486StackPush(State, OldEsp)) return FALSE;
240 }
241 }
242 else
243 {
244 if (State->SegmentRegs[FAST486_REG_CS].Size)
245 {
246 /* Set OPSIZE, because INT always pushes 16-bit values in real mode */
247 State->PrefixFlags |= FAST486_PREFIX_OPSIZE;
248 }
249 }
250
251 /* Push EFLAGS */
252 if (!Fast486StackPush(State, State->Flags.Long)) return FALSE;
253
254 /* Push CS selector */
255 if (!Fast486StackPush(State, State->SegmentRegs[FAST486_REG_CS].Selector)) return FALSE;
256
257 /* Push the instruction pointer */
258 if (!Fast486StackPush(State, State->InstPtr.Long)) return FALSE;
259
260 if (InterruptGate)
261 {
262 /* Disable interrupts after a jump to an interrupt gate handler */
263 State->Flags.If = FALSE;
264 }
265
266 /* Load new CS */
267 if (!Fast486LoadSegment(State, FAST486_REG_CS, SegmentSelector))
268 {
269 /* An exception occurred during the jump */
270 return FALSE;
271 }
272
273 if (State->SegmentRegs[FAST486_REG_CS].Size)
274 {
275 /* 32-bit code segment, use EIP */
276 State->InstPtr.Long = Offset;
277 }
278 else
279 {
280 /* 16-bit code segment, use IP */
281 State->InstPtr.LowWord = LOWORD(Offset);
282 }
283
284 return TRUE;
285 }
286
287 VOID
288 FASTCALL
289 Fast486ExceptionWithErrorCode(PFAST486_STATE State,
290 FAST486_EXCEPTIONS ExceptionCode,
291 ULONG ErrorCode)
292 {
293 FAST486_IDT_ENTRY IdtEntry;
294
295 /* Increment the exception count */
296 State->ExceptionCount++;
297
298 /* Check if the exception occurred more than once */
299 if (State->ExceptionCount > 1)
300 {
301 /* Then this is a double fault */
302 ExceptionCode = FAST486_EXCEPTION_DF;
303 }
304
305 /* Check if this is a triple fault */
306 if (State->ExceptionCount == 3)
307 {
308 /* Reset the CPU */
309 Fast486Reset(State);
310 return;
311 }
312
313 /* Restore the IP to the saved IP */
314 State->InstPtr = State->SavedInstPtr;
315
316 if (!Fast486GetIntVector(State, ExceptionCode, &IdtEntry))
317 {
318 /*
319 * If this function failed, that means Fast486Exception
320 * was called again, so just return in this case.
321 */
322 return;
323 }
324
325 /* Perform the interrupt */
326 if (!Fast486InterruptInternal(State,
327 IdtEntry.Selector,
328 MAKELONG(IdtEntry.Offset, IdtEntry.OffsetHigh),
329 IdtEntry.Type))
330 {
331 /*
332 * If this function failed, that means Fast486Exception
333 * was called again, so just return in this case.
334 */
335 return;
336 }
337
338 if (EXCEPTION_HAS_ERROR_CODE(ExceptionCode)
339 && (State->ControlRegisters[FAST486_REG_CR0] & FAST486_CR0_PE))
340 {
341 /* Push the error code */
342 if (!Fast486StackPush(State, ErrorCode))
343 {
344 /*
345 * If this function failed, that means Fast486Exception
346 * was called again, so just return in this case.
347 */
348 return;
349 }
350 }
351
352 /* Reset the exception count */
353 State->ExceptionCount = 0;
354 }
355
356 /* EOF */