summaryrefslogtreecommitdiffstats
path: root/cpukit/score/cpu/i386/cpu_asm.S
blob: bb5c096e7204f7c0b13f3b74776eed88baa6ce20 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
/* SPDX-License-Identifier: BSD-2-Clause */

/**
 *  @file
 *
 *  This file contains all assembly code for the Intel i386 implementation
 *  of RTEMS.
 */

/*
 *  COPYRIGHT (c) 1989-1999.
 *  On-Line Applications Research Corporation (OAR).
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <rtems/asm.h>
#include <rtems/score/cpu.h>

#ifndef CPU_STACK_ALIGNMENT
#error "Missing header? CPU_STACK_ALIGNMENT not defined"
#endif

/*
 * Format of i386 Register structure
 */

.set REG_EFLAGS,  I386_CONTEXT_CONTROL_EFLAGS_OFFSET
.set REG_ESP,     I386_CONTEXT_CONTROL_ESP_OFFSET
.set REG_EBP,     I386_CONTEXT_CONTROL_EBP_OFFSET
.set REG_EBX,     I386_CONTEXT_CONTROL_EBX_OFFSET
.set REG_ESI,     I386_CONTEXT_CONTROL_ESI_OFFSET
.set REG_EDI,     I386_CONTEXT_CONTROL_EDI_OFFSET
.set REG_GS_0,    I386_CONTEXT_CONTROL_GS_0_OFFSET
.set REG_GS_1,    I386_CONTEXT_CONTROL_GS_1_OFFSET

        BEGIN_CODE

/*
 *  void _CPU_Context_switch( run_context, heir_context )
 *
 *  This routine performs a normal non-FP context.
 */

        .p2align  1
        PUBLIC (_CPU_Context_switch)
        PUBLIC (_CPU_Context_switch_no_return)

.set RUNCONTEXT_ARG,   4                   /* save context argument */
.set HEIRCONTEXT_ARG,  8                   /* restore context argument */

SYM (_CPU_Context_switch):
SYM (_CPU_Context_switch_no_return):
        movl      RUNCONTEXT_ARG(esp),eax  /* eax = running threads context */
        GET_SELF_CPU_CONTROL edx           /* edx has address for per_CPU information */
        movl      PER_CPU_ISR_DISPATCH_DISABLE(edx),ecx
        pushf                              /* push eflags */
        popl      REG_EFLAGS(eax)          /* save eflags */
        movl      esp,REG_ESP(eax)         /* save stack pointer */
        movl      ebp,REG_EBP(eax)         /* save base pointer */
        movl      ebx,REG_EBX(eax)         /* save ebx */
        movl      esi,REG_ESI(eax)         /* save source register */
        movl      edi,REG_EDI(eax)         /* save destination register */
        movl      ecx, I386_CONTEXT_CONTROL_ISR_DISPATCH_DISABLE(eax)

        movl      eax,ecx                  /* ecx = running threads context */
        movl      HEIRCONTEXT_ARG(esp),eax /* eax = heir threads context */

#ifdef RTEMS_SMP
      /*
       * The executing thread no longer executes on this processor.  Switch
       * the stack to the temporary interrupt stack of this processor.  Mark
       * the context of the executing thread as not executing.
       */
        leal      PER_CPU_INTERRUPT_FRAME_AREA + CPU_INTERRUPT_FRAME_SIZE(edx),esp
        movb      $0, I386_CONTEXT_CONTROL_IS_EXECUTING_OFFSET(ecx)

.L_check_is_executing:
        lock bts  $0,I386_CONTEXT_CONTROL_IS_EXECUTING_OFFSET(eax)  /* Indicator in carry flag */
        jc        .L_get_potential_new_heir
#endif

/* Start restoring context */
.L_restore:
        movl      I386_CONTEXT_CONTROL_ISR_DISPATCH_DISABLE(eax),ecx
        movl      ecx,PER_CPU_ISR_DISPATCH_DISABLE(edx)
        movl      REG_ESP(eax),esp         /* restore stack pointer */
        pushl     REG_EFLAGS(eax)          /* push eflags */
        popf                               /* restore eflags */
        movl      REG_EBP(eax),ebp         /* restore base pointer */
        movl      REG_EBX(eax),ebx         /* restore ebx */
        movl      REG_ESI(eax),esi         /* restore source register */
        movl      REG_EDI(eax),edi         /* restore destination register */
        GET_CPU_ID ecx
        movl      REG_GS_0(eax), edx       /* restore gs segment */
        movl      edx, _Global_descriptor_table+24(,ecx,8)
        movl      REG_GS_1(eax), edx
        movl      edx, _Global_descriptor_table+28(,ecx,8)
        leal      24(,ecx,8), edx
        movl      edx, gs
        ret

/*
 *  NOTE: May be unnecessary to reload some registers.
 */

/*
 *  void _CPU_Context_restore( new_context )
 *
 *  This routine performs a normal non-FP context.
 */

        PUBLIC (_CPU_Context_restore)

.set NEWCONTEXT_ARG,   4                   /* context to restore argument */

SYM (_CPU_Context_restore):
        movl      NEWCONTEXT_ARG(esp),eax  /* eax = running threads context */
        GET_SELF_CPU_CONTROL edx           /* edx has address for per_CPU information */
        jmp       .L_restore

#ifdef RTEMS_SMP

.L_get_potential_new_heir:

        /* We may have a new heir */

        /* Read the executing and heir */
        movl    PER_CPU_OFFSET_EXECUTING(edx),ebx
        movl    PER_CPU_OFFSET_HEIR(edx),esi

        /*
         * Update the executing only if necessary to avoid cache line
         * monopolization.
         */
        cmp     esi,ebx
        je      .L_check_is_executing

        /* Calculate the heir context pointer */
        addl    esi,eax
        subl    ebx,eax

        /* Update the executing */
        movl    esi,PER_CPU_OFFSET_EXECUTING(edx)

        jmp     .L_check_is_executing
#endif

/*void _CPU_Context_save_fp_context( &fp_context_ptr )
 *  void _CPU_Context_restore_fp_context( &fp_context_ptr )
 *
 *  This section is used to context switch an i80287, i80387,
 *  the built-in coprocessor or the i80486 or compatible.
 */

.set FPCONTEXT_ARG,   4                    /* FP context argument */

#ifndef __SSE__
        .p2align  1
        PUBLIC (_CPU_Context_save_fp)
SYM (_CPU_Context_save_fp):
        movl      FPCONTEXT_ARG(esp),eax   /* eax = &ptr to FP context area */
        movl      (eax),eax                /* eax = FP context area */
        fsave     (eax)                    /* save FP context */
        ret

        .p2align  1
        PUBLIC (_CPU_Context_restore_fp)
SYM (_CPU_Context_restore_fp):
        movl      FPCONTEXT_ARG(esp),eax   /* eax = &ptr to FP context area */
        movl      (eax),eax                /* eax = FP context area */
        frstor    (eax)                    /* restore FP context */
        ret
#endif

#ifdef __SSE__
#define SSE_OFF  16
#endif

        PUBLIC (_Exception_Handler)
SYM (_Exception_Handler):
	pusha                  /* Push general purpose registers    */
	pushl   $0             /* Null pointer to SSE area          */
	movl    esp,  ebp      /* Save original SP                  */
#ifndef __SSE__
	subl    $4,   esp      /* Reserve space for argument        */
                           /* Align stack (courtesy for C/gcc)  */
	andl    $ - CPU_STACK_ALIGNMENT, esp
#else
	subl    $512, esp      /* Space for SSE area                */
                           /* Align stack (courtesy for C/gcc)  */
	andl    $ - CPU_STACK_ALIGNMENT, esp
/* Doing fwait here will re-throw an already pending FP exception!
	fwait
 */
	fxsave  0(esp)
	fninit                 /* Clean-slate FPU                   */
	movl    $0x1f80, 0(ebp)
	ldmxcsr 0(ebp)         /* Clean-slate MXCSR                 */
	movl    esp, 0(ebp)    /* Store pointer to SSE area         */
	subl    $SSE_OFF, esp  /* Aligned space for argument        */
#endif
	movl    ebp, (esp)     /* Store argument                    */
	movl	_currentExcHandler, eax	   /* Call function stored in _currentExcHandler */
	call	* eax
#ifdef __SSE__
	fwait
	fxrstor 16(esp)
#endif
	movl    ebp,  esp      /* Restore original SP               */
	addl    $4,   esp      /* Skill pointer to SSE area         */
	popa                   /* Restore general purpose registers */
	addl	$8, esp        /* Skill vector number and faultCode */
	iret

#define DISTINCT_EXCEPTION_WITH_FAULTCODE_ENTRY(_vector) \
        .p2align 4                         ; \
        PUBLIC (rtems_exception_prologue_ ## _vector ) ; \
SYM (rtems_exception_prologue_ ## _vector ):             \
	pushl	$ _vector	; \
        jmp   SYM (_Exception_Handler) ;

#define DISTINCT_EXCEPTION_WITHOUT_FAULTCODE_ENTRY(_vector) \
        .p2align 4                         ; \
        PUBLIC (rtems_exception_prologue_ ## _vector ) ; \
SYM (rtems_exception_prologue_ ## _vector ):             \
	pushl	$ 0		; \
	pushl	$ _vector	; \
        jmp   SYM (_Exception_Handler) ;

/*
 * Divide Error
 */
DISTINCT_EXCEPTION_WITHOUT_FAULTCODE_ENTRY (0)
/*
 * Debug Exception
 */
DISTINCT_EXCEPTION_WITHOUT_FAULTCODE_ENTRY (1)
/*
 * NMI
 */
DISTINCT_EXCEPTION_WITHOUT_FAULTCODE_ENTRY (2)
/*
 * Breakpoint
 */
DISTINCT_EXCEPTION_WITHOUT_FAULTCODE_ENTRY (3)
/*
 * Overflow
 */
DISTINCT_EXCEPTION_WITHOUT_FAULTCODE_ENTRY (4)
/*
 * Bound Range Exceeded
 */
DISTINCT_EXCEPTION_WITHOUT_FAULTCODE_ENTRY (5)
/*
 * Invalid Opcode
 */
DISTINCT_EXCEPTION_WITHOUT_FAULTCODE_ENTRY (6)
/*
 * No Math Coproc
 */
DISTINCT_EXCEPTION_WITHOUT_FAULTCODE_ENTRY (7)
/*
 * Double Fault
 */
DISTINCT_EXCEPTION_WITH_FAULTCODE_ENTRY (8)
/*
 * Coprocessor segment overrun
 */
DISTINCT_EXCEPTION_WITHOUT_FAULTCODE_ENTRY (9)
/*
 * Invalid TSS
 */
DISTINCT_EXCEPTION_WITH_FAULTCODE_ENTRY (10)
/*
 * Segment Not Present
 */
DISTINCT_EXCEPTION_WITH_FAULTCODE_ENTRY (11)
/*
 * Stack segment Fault
 */
DISTINCT_EXCEPTION_WITH_FAULTCODE_ENTRY (12)
/*
 * General Protection Fault
 */
DISTINCT_EXCEPTION_WITH_FAULTCODE_ENTRY (13)
/*
 * Page Fault
 */
DISTINCT_EXCEPTION_WITH_FAULTCODE_ENTRY (14)
/*
 * Floating point error (NB 15 is reserved it is therefor skipped)
 */
DISTINCT_EXCEPTION_WITHOUT_FAULTCODE_ENTRY (16)
/*
 * Aligment Check
 */
DISTINCT_EXCEPTION_WITH_FAULTCODE_ENTRY (17)
/*
 * Machine Check
 */
DISTINCT_EXCEPTION_WITH_FAULTCODE_ENTRY (18)

#ifdef __SSE__
/*
 * SIMD FP Exception
 */
DISTINCT_EXCEPTION_WITHOUT_FAULTCODE_ENTRY (19)
#endif


/*
 *  void *i386_Logical_to_physical(
 *     uint16_t    segment,
 *     void             *address
 *  );
 *
 *  Returns thirty-two bit physical address for segment:address.
 */

.set SEGMENT_ARG, 4
.set ADDRESS_ARG, 8

             PUBLIC (i386_Logical_to_physical)

SYM (i386_Logical_to_physical):

        xorl    eax,eax                /* clear eax */
        movzwl  SEGMENT_ARG(esp),ecx   /* ecx = segment value */
        movl    $ SYM (_Global_descriptor_table),edx
                                       /* edx = address of our GDT */
        addl    ecx,edx                /* edx = address of desired entry */
        movb    7(edx),ah              /* ah = base 31:24 */
        movb    4(edx),al              /* al = base 23:16 */
        shll    $16,eax                /* move ax into correct bits */
        movw    2(edx),ax              /* ax = base 0:15 */
        movl    ADDRESS_ARG(esp),ecx   /* ecx = address to convert */
        addl    eax,ecx                /* ecx = physical address equivalent */
        movl    ecx,eax                /* eax = ecx */
        ret

/*
 *  void *i386_Physical_to_logical(
 *     uint16_t    segment,
 *     void             *address
 *  );
 *
 *  Returns thirty-two bit physical address for segment:address.
 */

/*
 *.set SEGMENT_ARG, 4
 *.set ADDRESS_ARG, 8   -- use sets from above
 */

       PUBLIC (i386_Physical_to_logical)

SYM (i386_Physical_to_logical):
        xorl    eax,eax                /* clear eax */
        movzwl  SEGMENT_ARG(esp),ecx   /* ecx = segment value */
        movl    $ SYM (_Global_descriptor_table),edx
                                       /* edx = address of our GDT */
        addl    ecx,edx                /* edx = address of desired entry */
        movb    7(edx),ah              /* ah = base 31:24 */
        movb    4(edx),al              /* al = base 23:16 */
        shll    $16,eax                /* move ax into correct bits */
        movw    2(edx),ax              /* ax = base 0:15 */
        movl    ADDRESS_ARG(esp),ecx   /* ecx = address to convert */
        subl    eax,ecx                /* ecx = logical address equivalent */
        movl    ecx,eax                /* eax = ecx */
        ret

/*
 *  int i386_Physical_to_real(
 *     void     *address,
 *     uint16_t *segment,
 *     uint16_t *offset
 *  );
 *
 *  Fills segment:offest real mode pointer counted from thirty-two bit physical
 *  address.
 *  Returns 0 if inconvertible, 1 if successfuly converted.
 */

.set PHYS_PTR_ARG,   4
.set RM_PTR_SEG_ARG, 8
.set RM_PTR_OFF_ARG, 12

       PUBLIC (i386_Physical_to_real)

SYM (i386_Physical_to_real):
        movl    PHYS_PTR_ARG(esp),eax
        cmpl    $0x10FFF0, eax
        js      1f
        movl    $0, eax
        ret
1:  	cmpl    $0x100000, eax
        js      2f
        subl    $0xFFFF0, eax
        movl    RM_PTR_OFF_ARG(esp), ecx
        movw    ax, (ecx)
        movl    RM_PTR_SEG_ARG(esp), ecx
        movw    $0xFFFF, (ecx)
        movl    $1, eax
        ret
2: 	movl    eax, edx
        and     $0xF, ax
        movl    RM_PTR_OFF_ARG(esp), ecx
        movw    ax, (ecx)
        shrl    $4, edx
        movl    RM_PTR_SEG_ARG(esp), ecx
        movw    dx, (ecx)
        movl    $1, eax
        ret

END_CODE

END