1 | /* cpu_asm.s |
---|
2 | * |
---|
3 | * This file contains all assembly code for the Intel i386 implementation |
---|
4 | * of RTEMS. |
---|
5 | * |
---|
6 | * COPYRIGHT (c) 1989-1999. |
---|
7 | * On-Line Applications Research Corporation (OAR). |
---|
8 | * |
---|
9 | * The license and distribution terms for this file may be |
---|
10 | * found in the file LICENSE in this distribution or at |
---|
11 | * http://www.rtems.org/license/LICENSE. |
---|
12 | */ |
---|
13 | |
---|
14 | #ifdef HAVE_CONFIG_H |
---|
15 | #include "config.h" |
---|
16 | #endif |
---|
17 | |
---|
18 | #include <rtems/asm.h> |
---|
19 | #include <rtems/score/cpu.h> |
---|
20 | |
---|
21 | #ifndef CPU_STACK_ALIGNMENT |
---|
22 | #error "Missing header? CPU_STACK_ALIGNMENT not defined" |
---|
23 | #endif |
---|
24 | |
---|
25 | /* |
---|
26 | * Format of i386 Register structure |
---|
27 | */ |
---|
28 | |
---|
29 | .set REG_EFLAGS, I386_CONTEXT_CONTROL_EFLAGS_OFFSET |
---|
30 | .set REG_ESP, I386_CONTEXT_CONTROL_ESP_OFFSET |
---|
31 | .set REG_EBP, I386_CONTEXT_CONTROL_EBP_OFFSET |
---|
32 | .set REG_EBX, I386_CONTEXT_CONTROL_EBX_OFFSET |
---|
33 | .set REG_ESI, I386_CONTEXT_CONTROL_ESI_OFFSET |
---|
34 | .set REG_EDI, I386_CONTEXT_CONTROL_EDI_OFFSET |
---|
35 | .set REG_GS_0, I386_CONTEXT_CONTROL_GS_0_OFFSET |
---|
36 | .set REG_GS_1, I386_CONTEXT_CONTROL_GS_1_OFFSET |
---|
37 | |
---|
38 | BEGIN_CODE |
---|
39 | |
---|
40 | /* |
---|
41 | * void _CPU_Context_switch( run_context, heir_context ) |
---|
42 | * |
---|
43 | * This routine performs a normal non-FP context. |
---|
44 | */ |
---|
45 | |
---|
46 | .p2align 1 |
---|
47 | PUBLIC (_CPU_Context_switch) |
---|
48 | |
---|
49 | .set RUNCONTEXT_ARG, 4 /* save context argument */ |
---|
50 | .set HEIRCONTEXT_ARG, 8 /* restore context argument */ |
---|
51 | |
---|
52 | SYM (_CPU_Context_switch): |
---|
53 | movl RUNCONTEXT_ARG(esp),eax /* eax = running threads context */ |
---|
54 | pushf /* push eflags */ |
---|
55 | popl REG_EFLAGS(eax) /* save eflags */ |
---|
56 | movl esp,REG_ESP(eax) /* save stack pointer */ |
---|
57 | movl ebp,REG_EBP(eax) /* save base pointer */ |
---|
58 | movl ebx,REG_EBX(eax) /* save ebx */ |
---|
59 | movl esi,REG_ESI(eax) /* save source register */ |
---|
60 | movl edi,REG_EDI(eax) /* save destination register */ |
---|
61 | |
---|
62 | #ifdef RTEMS_SMP |
---|
63 | /* The executing context no longer executes on this processor */ |
---|
64 | movb $0, I386_CONTEXT_CONTROL_IS_EXECUTING_OFFSET(eax) |
---|
65 | #endif |
---|
66 | |
---|
67 | movl HEIRCONTEXT_ARG(esp),eax /* eax = heir threads context */ |
---|
68 | |
---|
69 | #ifdef RTEMS_SMP |
---|
70 | /* Wait for heir context to stop execution */ |
---|
71 | 1: |
---|
72 | movb I386_CONTEXT_CONTROL_IS_EXECUTING_OFFSET(eax), bl |
---|
73 | testb bl, bl |
---|
74 | jne 1b |
---|
75 | |
---|
76 | /* The heir context executes now on this processor */ |
---|
77 | movb $1, I386_CONTEXT_CONTROL_IS_EXECUTING_OFFSET(eax) |
---|
78 | #endif |
---|
79 | |
---|
80 | restore: |
---|
81 | pushl REG_EFLAGS(eax) /* push eflags */ |
---|
82 | popf /* restore eflags */ |
---|
83 | movl REG_ESP(eax),esp /* restore stack pointer */ |
---|
84 | movl REG_EBP(eax),ebp /* restore base pointer */ |
---|
85 | movl REG_EBX(eax),ebx /* restore ebx */ |
---|
86 | movl REG_ESI(eax),esi /* restore source register */ |
---|
87 | movl REG_EDI(eax),edi /* restore destination register */ |
---|
88 | movl REG_GS_0(eax), ecx /* restore gs segment */ |
---|
89 | movl REG_GS_1(eax), edx |
---|
90 | movl ecx, _Global_descriptor_table + 24 |
---|
91 | movl edx, _Global_descriptor_table + 28 |
---|
92 | movl $24, ecx |
---|
93 | mov ecx, gs |
---|
94 | ret |
---|
95 | |
---|
96 | /* |
---|
97 | * NOTE: May be unnecessary to reload some registers. |
---|
98 | */ |
---|
99 | |
---|
100 | /* |
---|
101 | * void _CPU_Context_restore( new_context ) |
---|
102 | * |
---|
103 | * This routine performs a normal non-FP context. |
---|
104 | */ |
---|
105 | |
---|
106 | PUBLIC (_CPU_Context_restore) |
---|
107 | |
---|
108 | .set NEWCONTEXT_ARG, 4 /* context to restore argument */ |
---|
109 | |
---|
110 | SYM (_CPU_Context_restore): |
---|
111 | movl NEWCONTEXT_ARG(esp),eax /* eax = running threads context */ |
---|
112 | jmp restore |
---|
113 | |
---|
114 | /*void _CPU_Context_save_fp_context( &fp_context_ptr ) |
---|
115 | * void _CPU_Context_restore_fp_context( &fp_context_ptr ) |
---|
116 | * |
---|
117 | * This section is used to context switch an i80287, i80387, |
---|
118 | * the built-in coprocessor or the i80486 or compatible. |
---|
119 | */ |
---|
120 | |
---|
121 | .set FPCONTEXT_ARG, 4 /* FP context argument */ |
---|
122 | |
---|
123 | #ifndef __SSE__ |
---|
124 | .p2align 1 |
---|
125 | PUBLIC (_CPU_Context_save_fp) |
---|
126 | SYM (_CPU_Context_save_fp): |
---|
127 | movl FPCONTEXT_ARG(esp),eax /* eax = &ptr to FP context area */ |
---|
128 | movl (eax),eax /* eax = FP context area */ |
---|
129 | fsave (eax) /* save FP context */ |
---|
130 | ret |
---|
131 | |
---|
132 | .p2align 1 |
---|
133 | PUBLIC (_CPU_Context_restore_fp) |
---|
134 | SYM (_CPU_Context_restore_fp): |
---|
135 | movl FPCONTEXT_ARG(esp),eax /* eax = &ptr to FP context area */ |
---|
136 | movl (eax),eax /* eax = FP context area */ |
---|
137 | frstor (eax) /* restore FP context */ |
---|
138 | ret |
---|
139 | #endif |
---|
140 | |
---|
141 | #ifdef __SSE__ |
---|
142 | #define SSE_OFF 16 |
---|
143 | #endif |
---|
144 | |
---|
145 | PUBLIC (_Exception_Handler) |
---|
146 | SYM (_Exception_Handler): |
---|
147 | pusha /* Push general purpose registers */ |
---|
148 | pushl $0 /* Null pointer to SSE area */ |
---|
149 | movl esp, ebp /* Save original SP */ |
---|
150 | #ifndef __SSE__ |
---|
151 | subl $4, esp /* Reserve space for argument */ |
---|
152 | /* Align stack (courtesy for C/gcc) */ |
---|
153 | andl $ - CPU_STACK_ALIGNMENT, esp |
---|
154 | #else |
---|
155 | subl $512, esp /* Space for SSE area */ |
---|
156 | /* Align stack (courtesy for C/gcc) */ |
---|
157 | andl $ - CPU_STACK_ALIGNMENT, esp |
---|
158 | /* Doing fwait here will re-throw an already pending FP exception! |
---|
159 | fwait |
---|
160 | */ |
---|
161 | fxsave 0(esp) |
---|
162 | fninit /* Clean-slate FPU */ |
---|
163 | movl $0x1f80, 0(ebp) |
---|
164 | ldmxcsr 0(ebp) /* Clean-slate MXCSR */ |
---|
165 | movl esp, 0(ebp) /* Store pointer to SSE area */ |
---|
166 | subl $SSE_OFF, esp /* Aligned space for argument */ |
---|
167 | #endif |
---|
168 | movl ebp, (esp) /* Store argument */ |
---|
169 | movl _currentExcHandler, eax /* Call function stored in _currentExcHandler */ |
---|
170 | call * eax |
---|
171 | #ifdef __SSE__ |
---|
172 | fwait |
---|
173 | fxrstor 16(esp) |
---|
174 | #endif |
---|
175 | movl ebp, esp /* Restore original SP */ |
---|
176 | addl $4, esp /* Skill pointer to SSE area */ |
---|
177 | popa /* Restore general purpose registers */ |
---|
178 | addl $8, esp /* Skill vector number and faultCode */ |
---|
179 | iret |
---|
180 | |
---|
181 | #define DISTINCT_EXCEPTION_WITH_FAULTCODE_ENTRY(_vector) \ |
---|
182 | .p2align 4 ; \ |
---|
183 | PUBLIC (rtems_exception_prologue_ ## _vector ) ; \ |
---|
184 | SYM (rtems_exception_prologue_ ## _vector ): \ |
---|
185 | pushl $ _vector ; \ |
---|
186 | jmp SYM (_Exception_Handler) ; |
---|
187 | |
---|
188 | #define DISTINCT_EXCEPTION_WITHOUT_FAULTCODE_ENTRY(_vector) \ |
---|
189 | .p2align 4 ; \ |
---|
190 | PUBLIC (rtems_exception_prologue_ ## _vector ) ; \ |
---|
191 | SYM (rtems_exception_prologue_ ## _vector ): \ |
---|
192 | pushl $ 0 ; \ |
---|
193 | pushl $ _vector ; \ |
---|
194 | jmp SYM (_Exception_Handler) ; |
---|
195 | |
---|
196 | /* |
---|
197 | * Divide Error |
---|
198 | */ |
---|
199 | DISTINCT_EXCEPTION_WITHOUT_FAULTCODE_ENTRY (0) |
---|
200 | /* |
---|
201 | * Debug Exception |
---|
202 | */ |
---|
203 | DISTINCT_EXCEPTION_WITHOUT_FAULTCODE_ENTRY (1) |
---|
204 | /* |
---|
205 | * NMI |
---|
206 | */ |
---|
207 | DISTINCT_EXCEPTION_WITHOUT_FAULTCODE_ENTRY (2) |
---|
208 | /* |
---|
209 | * Breakpoint |
---|
210 | */ |
---|
211 | DISTINCT_EXCEPTION_WITHOUT_FAULTCODE_ENTRY (3) |
---|
212 | /* |
---|
213 | * Overflow |
---|
214 | */ |
---|
215 | DISTINCT_EXCEPTION_WITHOUT_FAULTCODE_ENTRY (4) |
---|
216 | /* |
---|
217 | * Bound Range Exceeded |
---|
218 | */ |
---|
219 | DISTINCT_EXCEPTION_WITHOUT_FAULTCODE_ENTRY (5) |
---|
220 | /* |
---|
221 | * Invalid Opcode |
---|
222 | */ |
---|
223 | DISTINCT_EXCEPTION_WITHOUT_FAULTCODE_ENTRY (6) |
---|
224 | /* |
---|
225 | * No Math Coproc |
---|
226 | */ |
---|
227 | DISTINCT_EXCEPTION_WITHOUT_FAULTCODE_ENTRY (7) |
---|
228 | /* |
---|
229 | * Double Fault |
---|
230 | */ |
---|
231 | DISTINCT_EXCEPTION_WITH_FAULTCODE_ENTRY (8) |
---|
232 | /* |
---|
233 | * Coprocessor segment overrun |
---|
234 | */ |
---|
235 | DISTINCT_EXCEPTION_WITHOUT_FAULTCODE_ENTRY (9) |
---|
236 | /* |
---|
237 | * Invalid TSS |
---|
238 | */ |
---|
239 | DISTINCT_EXCEPTION_WITH_FAULTCODE_ENTRY (10) |
---|
240 | /* |
---|
241 | * Segment Not Present |
---|
242 | */ |
---|
243 | DISTINCT_EXCEPTION_WITH_FAULTCODE_ENTRY (11) |
---|
244 | /* |
---|
245 | * Stack segment Fault |
---|
246 | */ |
---|
247 | DISTINCT_EXCEPTION_WITH_FAULTCODE_ENTRY (12) |
---|
248 | /* |
---|
249 | * General Protection Fault |
---|
250 | */ |
---|
251 | DISTINCT_EXCEPTION_WITH_FAULTCODE_ENTRY (13) |
---|
252 | /* |
---|
253 | * Page Fault |
---|
254 | */ |
---|
255 | DISTINCT_EXCEPTION_WITH_FAULTCODE_ENTRY (14) |
---|
256 | /* |
---|
257 | * Floating point error (NB 15 is reserved it is therefor skipped) |
---|
258 | */ |
---|
259 | DISTINCT_EXCEPTION_WITHOUT_FAULTCODE_ENTRY (16) |
---|
260 | /* |
---|
261 | * Aligment Check |
---|
262 | */ |
---|
263 | DISTINCT_EXCEPTION_WITH_FAULTCODE_ENTRY (17) |
---|
264 | /* |
---|
265 | * Machine Check |
---|
266 | */ |
---|
267 | DISTINCT_EXCEPTION_WITH_FAULTCODE_ENTRY (18) |
---|
268 | |
---|
269 | #ifdef __SSE__ |
---|
270 | /* |
---|
271 | * SIMD FP Exception |
---|
272 | */ |
---|
273 | DISTINCT_EXCEPTION_WITHOUT_FAULTCODE_ENTRY (19) |
---|
274 | #endif |
---|
275 | |
---|
276 | |
---|
277 | /* |
---|
278 | * void *i386_Logical_to_physical( |
---|
279 | * uint16_t segment, |
---|
280 | * void *address |
---|
281 | * ); |
---|
282 | * |
---|
283 | * Returns thirty-two bit physical address for segment:address. |
---|
284 | */ |
---|
285 | |
---|
286 | .set SEGMENT_ARG, 4 |
---|
287 | .set ADDRESS_ARG, 8 |
---|
288 | |
---|
289 | PUBLIC (i386_Logical_to_physical) |
---|
290 | |
---|
291 | SYM (i386_Logical_to_physical): |
---|
292 | |
---|
293 | xorl eax,eax /* clear eax */ |
---|
294 | movzwl SEGMENT_ARG(esp),ecx /* ecx = segment value */ |
---|
295 | movl $ SYM (_Global_descriptor_table),edx |
---|
296 | /* edx = address of our GDT */ |
---|
297 | addl ecx,edx /* edx = address of desired entry */ |
---|
298 | movb 7(edx),ah /* ah = base 31:24 */ |
---|
299 | movb 4(edx),al /* al = base 23:16 */ |
---|
300 | shll $16,eax /* move ax into correct bits */ |
---|
301 | movw 2(edx),ax /* ax = base 0:15 */ |
---|
302 | movl ADDRESS_ARG(esp),ecx /* ecx = address to convert */ |
---|
303 | addl eax,ecx /* ecx = physical address equivalent */ |
---|
304 | movl ecx,eax /* eax = ecx */ |
---|
305 | ret |
---|
306 | |
---|
307 | /* |
---|
308 | * void *i386_Physical_to_logical( |
---|
309 | * uint16_t segment, |
---|
310 | * void *address |
---|
311 | * ); |
---|
312 | * |
---|
313 | * Returns thirty-two bit physical address for segment:address. |
---|
314 | */ |
---|
315 | |
---|
316 | /* |
---|
317 | *.set SEGMENT_ARG, 4 |
---|
318 | *.set ADDRESS_ARG, 8 -- use sets from above |
---|
319 | */ |
---|
320 | |
---|
321 | PUBLIC (i386_Physical_to_logical) |
---|
322 | |
---|
323 | SYM (i386_Physical_to_logical): |
---|
324 | xorl eax,eax /* clear eax */ |
---|
325 | movzwl SEGMENT_ARG(esp),ecx /* ecx = segment value */ |
---|
326 | movl $ SYM (_Global_descriptor_table),edx |
---|
327 | /* edx = address of our GDT */ |
---|
328 | addl ecx,edx /* edx = address of desired entry */ |
---|
329 | movb 7(edx),ah /* ah = base 31:24 */ |
---|
330 | movb 4(edx),al /* al = base 23:16 */ |
---|
331 | shll $16,eax /* move ax into correct bits */ |
---|
332 | movw 2(edx),ax /* ax = base 0:15 */ |
---|
333 | movl ADDRESS_ARG(esp),ecx /* ecx = address to convert */ |
---|
334 | subl eax,ecx /* ecx = logical address equivalent */ |
---|
335 | movl ecx,eax /* eax = ecx */ |
---|
336 | ret |
---|
337 | |
---|
338 | /* |
---|
339 | * int i386_Physical_to_real( |
---|
340 | * void *address, |
---|
341 | * uint16_t *segment, |
---|
342 | * uint16_t *offset |
---|
343 | * ); |
---|
344 | * |
---|
345 | * Fills segment:offest real mode pointer counted from thirty-two bit physical |
---|
346 | * address. |
---|
347 | * Returns 0 if inconvertible, 1 if successfuly converted. |
---|
348 | */ |
---|
349 | |
---|
350 | .set PHYS_PTR_ARG, 4 |
---|
351 | .set RM_PTR_SEG_ARG, 8 |
---|
352 | .set RM_PTR_OFF_ARG, 12 |
---|
353 | |
---|
354 | PUBLIC (i386_Physical_to_real) |
---|
355 | |
---|
356 | SYM (i386_Physical_to_real): |
---|
357 | movl PHYS_PTR_ARG(esp),eax |
---|
358 | cmpl $0x10FFF0, eax |
---|
359 | js 1f |
---|
360 | movl $0, eax |
---|
361 | ret |
---|
362 | 1: cmpl $0x100000, eax |
---|
363 | js 2f |
---|
364 | subl $0xFFFF0, eax |
---|
365 | movl RM_PTR_OFF_ARG(esp), ecx |
---|
366 | movw ax, (ecx) |
---|
367 | movl RM_PTR_SEG_ARG(esp), ecx |
---|
368 | movw $0xFFFF, (ecx) |
---|
369 | movl $1, eax |
---|
370 | ret |
---|
371 | 2: movl eax, edx |
---|
372 | and $0xF, ax |
---|
373 | movl RM_PTR_OFF_ARG(esp), ecx |
---|
374 | movw ax, (ecx) |
---|
375 | shrl $4, edx |
---|
376 | movl RM_PTR_SEG_ARG(esp), ecx |
---|
377 | movw dx, (ecx) |
---|
378 | movl $1, eax |
---|
379 | ret |
---|
380 | |
---|
381 | END_CODE |
---|
382 | |
---|
383 | END |
---|