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
|
/* irq.c
*
* This file contains the implementation of the function described in irq.h
*
* Copyright (C) 1998 valette@crf.canon.fr
*
* COPYRIGHT (c) 1989-2011.
* On-Line Applications Research Corporation (OAR).
*
* The license and distribution terms for this file may be
* found in found in the file LICENSE in this distribution or at
* http://www.rtems.org/license/LICENSE.
*/
#include <rtems/asm.h>
#include <rtems/system.h>
#include <bspopts.h>
#include <bsp/irq_asm.h>
#include <rtems/score/cpu.h>
#include <rtems/score/percpu.h>
#ifndef CPU_STACK_ALIGNMENT
#error "Missing header? CPU_STACK_ALIGNMENT is not defined here"
#endif
/* Stack frame we use for intermediate storage */
#define ARG_OFF 0
#define MSK_OFF 4
#define EBX_OFF 8 /* ebx */
#define EBP_OFF 12 /* code restoring ebp/esp relies on */
#define ESP_OFF 16 /* esp being on top of ebp! */
#ifdef __SSE__
/* need to be on 16 byte boundary for SSE, add 12 to do that */
#define FRM_SIZ (20+12+512)
#define SSE_OFF 32
#else
#define FRM_SIZ 20
#endif
BEGIN_CODE
SYM (_ISR_Handler):
/*
* Before this was point is reached the vectors unique
* entry point did the following:
*
* 1. saved scratch registers registers eax edx ecx"
* 2. put the vector number in ecx.
*
* BEGINNING OF ESTABLISH SEGMENTS
*
* WARNING: If an interrupt can occur when the segments are
* not correct, then this is where we should establish
* the segments. In addition to establishing the
* segments, it may be necessary to establish a stack
* in the current data area on the outermost interrupt.
*
* NOTE: If the previous values of the segment registers are
* pushed, do not forget to adjust SAVED_REGS.
*
* NOTE: Make sure the exit code which restores these
* when this type of code is needed.
*/
/***** ESTABLISH SEGMENTS CODE GOES HERE ******/
/*
* END OF ESTABLISH SEGMENTS
*/
/*
* Establish an aligned stack frame
* original sp
* saved ebx
* saved ebp
* saved irq mask
* vector arg to C_dispatch_isr <- aligned SP
*/
movl esp, eax
subl $FRM_SIZ, esp
andl $ - CPU_STACK_ALIGNMENT, esp
movl ebx, EBX_OFF(esp)
movl eax, ESP_OFF(esp)
movl ebp, EBP_OFF(esp)
#ifdef __SSE__
/* NOTE: SSE only is supported if the BSP enables fxsave/fxrstor
* to save/restore SSE context! This is so far only implemented
* for pc386!.
*/
/* We save SSE here (on the task stack) because we possibly
* call other C-code (besides the ISR, namely _Thread_Dispatch())
*/
/* don't wait here; a possible exception condition will eventually be
* detected when the task resumes control and executes a FP instruction
fwait
*/
fxsave SSE_OFF(esp)
fninit /* clean-slate FPU */
movl $0x1f80, ARG_OFF(esp) /* use ARG_OFF as scratch space */
ldmxcsr ARG_OFF(esp) /* clean-slate MXCSR */
#endif
/* Do not disable any 8259 interrupts if this isn't from one */
cmp ecx, 16 /* is this a PIC IRQ? */
jge .check_stack_switch
/*
* acknowledge the interrupt
*/
movw SYM (i8259s_cache), ax /* save current i8259 interrupt mask */
movl eax, MSK_OFF(esp) /* save in stack frame */
/*
* compute the new PIC mask:
*
* <new mask> = <old mask> | irq_mask_or_tbl[<intr number aka ecx>]
*/
movw SYM (irq_mask_or_tbl) (,ecx,2), dx
orw dx, ax
/*
* Install new computed value on the i8259 and update cache
* accordingly
*/
movw ax, SYM (i8259s_cache)
outb $PIC_MASTER_IMR_IO_PORT
movb ah, al
outb $PIC_SLAVE_IMR_IO_PORT
movb $PIC_EOI, al
cmpl $7, ecx
jbe .master
outb $PIC_SLAVE_COMMAND_IO_PORT
.master:
outb $PIC_MASTER_COMMAND_IO_PORT
/*
* Now switch stacks if necessary
*/
PUBLIC (ISR_STOP)
ISR_STOP:
.check_stack_switch:
movl esp, ebp /* ebp = previous stack pointer */
#ifdef RTEMS_SMP
call SYM(_CPU_SMP_Get_current_processor)
sall $PER_CPU_CONTROL_SIZE_LOG2, eax
addl $SYM(_Per_CPU_Information), eax
movl eax, ebx
#else
movl $SYM(_Per_CPU_Information), ebx
#endif
/* is this the outermost interrupt? */
cmpl $0, PER_CPU_ISR_NEST_LEVEL(ebx)
jne nested /* No, then continue */
movl PER_CPU_INTERRUPT_STACK_HIGH(ebx), esp
/*
* We want to insure that the old stack pointer is in ebp
* By saving it on every interrupt, all we have to do is
* movl ebp->esp near the end of every interrupt.
*/
nested:
incl PER_CPU_ISR_NEST_LEVEL(ebx) /* one nest level deeper */
incl PER_CPU_THREAD_DISPATCH_DISABLE_LEVEL(ebx) /* disable
multitasking */
/*
* GCC versions starting with 4.3 no longer place the cld
* instruction before string operations. We need to ensure
* it is set correctly for ISR handlers.
*/
cld
/*
* re-enable interrupts at processor level as the current
* interrupt source is now masked via i8259
*/
sti
/*
* ECX is preloaded with the vector number; store as arg
* on top of stack. Note that _CPU_Interrupt_stack_high
* was adjusted in _CPU_Interrupt_stack_setup() (score/rtems/cpu.h)
* to make sure there is space.
*/
movl ecx, ARG_OFF(esp) /* store vector arg in stack */
call C_dispatch_isr
/*
* disable interrupts_again
*/
cli
movl ARG_OFF(esp), ecx /* grab vector arg from stack */
/*
* Restore stack. This moves back to the task stack
* when all interrupts are unnested.
*/
movl ebp, esp
/*
* restore the original i8259 masks
*/
/* Do not touch 8259 interrupts if this isn't from one */
cmp ecx, 16 /* is this a PIC IRQ? */
jge .dont_restore_i8259
movl MSK_OFF(esp), eax
movw ax, SYM (i8259s_cache)
outb $PIC_MASTER_IMR_IO_PORT
movb ah, al
outb $PIC_SLAVE_IMR_IO_PORT
.dont_restore_i8259:
decl PER_CPU_ISR_NEST_LEVEL(ebx) /* one less ISR nest level */
/* If interrupts are nested, */
/* then dispatching is disabled */
decl PER_CPU_THREAD_DISPATCH_DISABLE_LEVEL(ebx)
/* unnest multitasking */
/* Is dispatch disabled */
jne .exit /* Yes, then exit */
cmpb $0, PER_CPU_DISPATCH_NEEDED(ebx)
/* Is task switch necessary? */
jne .schedule /* Yes, then call the scheduler */
jmp .exit /* No, exit */
.schedule:
/*
* the scratch registers have already been saved and we are already
* back on the thread system stack. So we can call _Thread_Dispatch
* directly
*/
call _Thread_Dispatch
/*
* fall through exit to restore complete contex (scratch registers
* eip, CS, Flags).
*/
.exit:
#ifdef __SSE__
fwait
fxrstor SSE_OFF(esp)
#endif
/* restore ebx, ebp and original esp */
addl $EBX_OFF, esp
popl ebx
popl ebp
popl esp
/*
* BEGINNING OF DE-ESTABLISH SEGMENTS
*
* NOTE: Make sure there is code here if code is added to
* load the segment registers.
*
*/
/******* DE-ESTABLISH SEGMENTS CODE GOES HERE ********/
/*
* END OF DE-ESTABLISH SEGMENTS
*/
popl edx
popl ecx
popl eax
iret
#define DISTINCT_INTERRUPT_ENTRY(_vector) \
.p2align 4 ; \
PUBLIC (rtems_irq_prologue_ ## _vector ) ; \
SYM (rtems_irq_prologue_ ## _vector ): \
pushl eax ; \
pushl ecx ; \
pushl edx ; \
movl $ _vector, ecx ; \
jmp SYM (_ISR_Handler) ;
DISTINCT_INTERRUPT_ENTRY(0)
DISTINCT_INTERRUPT_ENTRY(1)
DISTINCT_INTERRUPT_ENTRY(2)
DISTINCT_INTERRUPT_ENTRY(3)
DISTINCT_INTERRUPT_ENTRY(4)
DISTINCT_INTERRUPT_ENTRY(5)
DISTINCT_INTERRUPT_ENTRY(6)
DISTINCT_INTERRUPT_ENTRY(7)
DISTINCT_INTERRUPT_ENTRY(8)
DISTINCT_INTERRUPT_ENTRY(9)
DISTINCT_INTERRUPT_ENTRY(10)
DISTINCT_INTERRUPT_ENTRY(11)
DISTINCT_INTERRUPT_ENTRY(12)
DISTINCT_INTERRUPT_ENTRY(13)
DISTINCT_INTERRUPT_ENTRY(14)
DISTINCT_INTERRUPT_ENTRY(15)
DISTINCT_INTERRUPT_ENTRY(16)
/*
* routine used to initialize the IDT by default
*/
PUBLIC (default_raw_idt_handler)
PUBLIC (raw_idt_notify)
SYM (default_raw_idt_handler):
pusha
cld
mov esp, ebp
andl $ - CPU_STACK_ALIGNMENT, esp
call raw_idt_notify
mov ebp, esp
popa
iret
END_CODE
END
|