source: rtems/cpukit/score/cpu/i386/cpu.c @ 2afb22b

5
Last change on this file since 2afb22b was 7b0c74ff, checked in by Sebastian Huber <sebastian.huber@…>, on 06/09/17 at 13:42:36

i386: Support thread-local storage (TLS)

Update #2468.

  • Property mode set to 100644
File size: 9.5 KB
Line 
1/**
2 *  @file
3 *
4 *  @brief Intel i386 Dependent Source
5 */
6
7/*
8 *  COPYRIGHT (c) 1989-1999.
9 *  On-Line Applications Research Corporation (OAR).
10 *
11 *  The license and distribution terms for this file may be
12 *  found in the file LICENSE in this distribution or at
13 *  http://www.rtems.org/license/LICENSE.
14 */
15
16#ifdef HAVE_CONFIG_H
17#include "config.h"
18#endif
19
20#include <inttypes.h>
21
22#include <rtems.h>
23#include <rtems/system.h>
24#include <rtems/score/types.h>
25#include <rtems/score/isr.h>
26#include <rtems/score/idtr.h>
27#include <rtems/score/tls.h>
28
29#include <rtems/bspIo.h>
30#include <rtems/score/percpu.h>
31#include <rtems/score/thread.h>
32
33#define I386_ASSERT_OFFSET(field, off) \
34  RTEMS_STATIC_ASSERT( \
35    offsetof(Context_Control, field) \
36      == I386_CONTEXT_CONTROL_ ## off ## _OFFSET, \
37    Context_Control_ ## field \
38  )
39
40I386_ASSERT_OFFSET(eflags, EFLAGS);
41I386_ASSERT_OFFSET(esp, ESP);
42I386_ASSERT_OFFSET(ebp, EBP);
43I386_ASSERT_OFFSET(ebx, EBX);
44I386_ASSERT_OFFSET(esi, ESI);
45I386_ASSERT_OFFSET(edi, EDI);
46
47RTEMS_STATIC_ASSERT(
48  offsetof(Context_Control, gs)
49    == I386_CONTEXT_CONTROL_GS_0_OFFSET,
50  Context_Control_gs_0
51);
52
53#ifdef RTEMS_SMP
54  I386_ASSERT_OFFSET(is_executing, IS_EXECUTING);
55#endif
56
57#if CPU_HARDWARE_FP
58Context_Control_fp _CPU_Null_fp_context;
59#endif
60
61void _CPU_Initialize(void)
62{
63#if CPU_HARDWARE_FP
64  register uint16_t             fp_status __asm__ ("ax");
65  register Context_Control_fp  *fp_context;
66#endif
67
68  /*
69   *  The following code saves a NULL i387 context which is given
70   *  to each task at start and restart time.  The following code
71   *  is based upon that provided in the i386 Programmer's
72   *  Manual and should work on any coprocessor greater than
73   *  the i80287.
74   *
75   *  NOTE: The NO WAIT form of the coprocessor instructions
76   *        MUST be used in case there is not a coprocessor
77   *        to wait for.
78   */
79
80#if CPU_HARDWARE_FP
81  fp_status = 0xa5a5;
82  __asm__ volatile( "fninit" );
83  __asm__ volatile( "fnstsw %0" : "=a" (fp_status) : "0" (fp_status) );
84
85  if ( fp_status ==  0 ) {
86
87    fp_context = &_CPU_Null_fp_context;
88
89#ifdef __SSE__
90        asm volatile( "fstcw %0":"=m"(fp_context->fpucw) );
91#else
92    __asm__ volatile( "fsave (%0)" : "=r" (fp_context)
93                               : "0"  (fp_context)
94                );
95#endif
96  }
97#endif
98
99#ifdef __SSE__
100
101  __asm__ volatile("stmxcsr %0":"=m"(fp_context->mxcsr));
102
103  /* The BSP must enable the SSE extensions (early).
104   * If any SSE instruction was already attempted
105   * then that crashed the system.
106   * As a courtesy, we double-check here but it
107   * may be too late (which is also why we don't
108   * enable SSE here).
109   */
110  {
111  uint32_t cr4;
112    __asm__ __volatile__("mov %%cr4, %0":"=r"(cr4));
113    if ( 0x600 != (cr4 & 0x600) ) {
114      printk("PANIC: RTEMS was compiled for SSE but BSP did not enable it"
115             "(CR4: 0%" PRIu32 ")\n", cr4);
116      while ( 1 ) {
117        __asm__ __volatile__("hlt");
118          }
119        }
120  }
121#endif
122}
123
124/*
125 * Stack alignment note:
126 *
127 * We want the stack to look to the '_entry_point' routine
128 * like an ordinary stack frame as if '_entry_point' was
129 * called from C-code.
130 * Note that '_entry_point' is jumped-to by the 'ret'
131 * instruction returning from _CPU_Context_switch() or
132 * _CPU_Context_restore() thus popping the _entry_point
133 * from the stack.
134 * However, _entry_point expects a frame to look like this:
135 *
136 *      args        [_Thread_Handler expects no args, however]
137 *      ------      (alignment boundary)
138 * SP-> return_addr return here when _entry_point returns which (never happens)
139 *
140 *
141 * Hence we must initialize the stack as follows
142 *
143 *         [arg1          ]:  n/a
144 *         [arg0 (aligned)]:  n/a
145 *         [ret. addr     ]:  NULL
146 * SP->    [jump-target   ]:  _entry_point
147 *
148 * When Context_switch returns it pops the _entry_point from
149 * the stack which then finds a standard layout.
150 */
151
152void _CPU_Context_Initialize(
153  Context_Control *the_context,
154  void *_stack_base,
155  size_t _size,
156  uint32_t _isr,
157  void (*_entry_point)( void ),
158  bool is_fp,
159  void *tls_area
160)
161{
162  uint32_t _stack;
163  uint32_t tcb;
164
165  (void) is_fp; /* avoid warning for being unused */
166
167  if ( _isr ) {
168    the_context->eflags = CPU_EFLAGS_INTERRUPTS_OFF;
169  } else {
170    the_context->eflags = CPU_EFLAGS_INTERRUPTS_ON;
171  }
172
173  _stack  = ((uint32_t)(_stack_base)) + (_size);
174  _stack &= ~ (CPU_STACK_ALIGNMENT - 1);
175  _stack -= 2*sizeof(proc_ptr*); /* see above for why we need to do this */
176  *((proc_ptr *)(_stack)) = (_entry_point);
177  the_context->ebp     = (void *) 0;
178  the_context->esp     = (void *) _stack;
179
180  if ( tls_area != NULL ) {
181    tcb = (uint32_t) _TLS_TCB_after_TLS_block_initialize( tls_area );
182  } else {
183    tcb = 0;
184  }
185
186  the_context->gs.limit_15_0 = 0xffff;
187  the_context->gs.base_address_15_0 = (tcb >> 0) & 0xffff;
188  the_context->gs.type = 0x2;
189  the_context->gs.descriptor_type = 0x1;
190  the_context->gs.limit_19_16 = 0xf;
191  the_context->gs.present = 0x1;
192  the_context->gs.operation_size = 0x1;
193  the_context->gs.granularity = 0x1;
194  the_context->gs.base_address_23_16 = (tcb >> 16) & 0xff;
195  the_context->gs.base_address_31_24 = (tcb >> 24) & 0xff;
196}
197
198uint32_t   _CPU_ISR_Get_level( void )
199{
200  uint32_t   level;
201
202#if !defined(RTEMS_PARAVIRT)
203  i386_get_interrupt_level( level );
204#else
205  level = i386_get_interrupt_level();
206#endif
207
208  return level;
209}
210
211struct Frame_ {
212        struct Frame_  *up;
213        uintptr_t               pc;
214};
215
216void _CPU_Exception_frame_print (const CPU_Exception_frame *ctx)
217{
218  unsigned int faultAddr = 0;
219  printk("----------------------------------------------------------\n");
220  printk("Exception %" PRIu32 " caught at PC %" PRIx32 " by thread %" PRId32 "\n",
221         ctx->idtIndex,
222         ctx->eip,
223         _Thread_Executing->Object.id);
224  printk("----------------------------------------------------------\n");
225  printk("Processor execution context at time of the fault was  :\n");
226  printk("----------------------------------------------------------\n");
227  printk(" EAX = %" PRIx32 "    EBX = %" PRIx32 "       ECX = %" PRIx32 "       EDX = %" PRIx32 "\n",
228         ctx->eax, ctx->ebx, ctx->ecx, ctx->edx);
229  printk(" ESI = %" PRIx32 "    EDI = %" PRIx32 "       EBP = %" PRIx32 "       ESP = %" PRIx32 "\n",
230         ctx->esi, ctx->edi, ctx->ebp, ctx->esp0);
231  printk("----------------------------------------------------------\n");
232  printk("Error code pushed by processor itself (if not 0) = %" PRIx32 "\n",
233         ctx->faultCode);
234  printk("----------------------------------------------------------\n");
235  if (ctx->idtIndex == I386_EXCEPTION_PAGE_FAULT){
236    faultAddr = i386_get_cr2();
237    printk("Page fault linear address (CR2) = %x\n", faultAddr);
238    printk("----------------------------------------------------------\n\n");
239  }
240 if (_ISR_Nest_level > 0) {
241    /*
242     * In this case we shall not delete the task interrupted as
243     * it has nothing to do with the fault. We cannot return either
244     * because the eip points to the faulty instruction so...
245     */
246    printk("Exception while executing ISR!!!. System locked\n");
247  }
248  else {
249        struct Frame_ *fp = (struct Frame_*)ctx->ebp;
250        int           i;
251
252        printk("Call Stack Trace of EIP:\n");
253        if ( fp ) {
254                for ( i=1; fp->up; fp=fp->up, i++ ) {
255                        printk("0x%08" PRIx32 " ",fp->pc);
256                        if ( ! (i&3) )
257                                printk("\n");
258                }
259        }
260        printk("\n");
261    /*
262     * OK I could probably use a simplified version but at least this
263     * should work.
264     */
265#if 0
266    printk(" ************ FAULTY THREAD WILL BE SUSPENDED **************\n");
267    rtems_task_suspend(_Thread_Executing->Object.id);
268#endif
269  }
270}
271
272static void _defaultExcHandler (CPU_Exception_frame *ctx)
273{
274  rtems_fatal(
275    RTEMS_FATAL_SOURCE_EXCEPTION,
276    (rtems_fatal_code) ctx
277  );
278}
279
280cpuExcHandlerType _currentExcHandler = _defaultExcHandler;
281
282extern void rtems_exception_prologue_0(void);
283extern void rtems_exception_prologue_1(void);
284extern void rtems_exception_prologue_2(void);
285extern void rtems_exception_prologue_3(void);
286extern void rtems_exception_prologue_4(void);
287extern void rtems_exception_prologue_5(void);
288extern void rtems_exception_prologue_6(void);
289extern void rtems_exception_prologue_7(void);
290extern void rtems_exception_prologue_8(void);
291extern void rtems_exception_prologue_9(void);
292extern void rtems_exception_prologue_10(void);
293extern void rtems_exception_prologue_11(void);
294extern void rtems_exception_prologue_12(void);
295extern void rtems_exception_prologue_13(void);
296extern void rtems_exception_prologue_14(void);
297extern void rtems_exception_prologue_16(void);
298extern void rtems_exception_prologue_17(void);
299extern void rtems_exception_prologue_18(void);
300#ifdef __SSE__
301extern void rtems_exception_prologue_19(void);
302#endif
303
304static rtems_raw_irq_hdl tbl[] = {
305         rtems_exception_prologue_0,
306         rtems_exception_prologue_1,
307         rtems_exception_prologue_2,
308         rtems_exception_prologue_3,
309         rtems_exception_prologue_4,
310         rtems_exception_prologue_5,
311         rtems_exception_prologue_6,
312         rtems_exception_prologue_7,
313         rtems_exception_prologue_8,
314         rtems_exception_prologue_9,
315         rtems_exception_prologue_10,
316         rtems_exception_prologue_11,
317         rtems_exception_prologue_12,
318         rtems_exception_prologue_13,
319         rtems_exception_prologue_14,
320     0,
321         rtems_exception_prologue_16,
322         rtems_exception_prologue_17,
323         rtems_exception_prologue_18,
324#ifdef __SSE__
325         rtems_exception_prologue_19,
326#endif
327};
328
329void rtems_exception_init_mngt(void)
330{
331      size_t                     i,j;
332      interrupt_gate_descriptor  *currentIdtEntry;
333      unsigned                   limit;
334      unsigned                   level;
335
336      i = sizeof(tbl) / sizeof (rtems_raw_irq_hdl);
337
338      i386_get_info_from_IDTR (&currentIdtEntry, &limit);
339
340      _CPU_ISR_Disable(level);
341      for (j = 0; j < i; j++) {
342        create_interrupt_gate_descriptor (&currentIdtEntry[j], tbl[j]);
343      }
344      _CPU_ISR_Enable(level);
345}
Note: See TracBrowser for help on using the repository browser.