summaryrefslogtreecommitdiffstats
path: root/c/src/lib/libbsp/i386/shared/irq/irq_asm.s
blob: 7765a9dd221102c520f95a9f125f649622386136 (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
/* irq.c
 *
 *  This file contains the implementation of the function described in irq.h
 *
 *  CopyRight (C) 1998 valette@crf.canon.fr
 *
 *  The license and distribution terms for this file may be
 *  found in found in the file LICENSE in this distribution or at
 *  http://www.OARcorp.com/rtems/license.html.
 *
 *  $Id$
 */

#include "asm.h"	
#include <irq_asm.h>

.set SAVED_REGS     , 32                   # space consumed by saved regs
.set EIP_OFFSET     , SAVED_REGS           # offset of tasks eip
.set CS_OFFSET      , EIP_OFFSET+4         # offset of tasks code segment
.set EFLAGS_OFFSET  , CS_OFFSET+4          # offset of tasks eflags
	

/*PAGE
 *  void _new_ISR_Displatch()
 *
 *  Entry point from the outermost interrupt service routine exit.
 *  The current stack is the supervisor mode stack.
 */

        PUBLIC (_new_ISR_Displatch)
SYM (_New_ISR_Displatch):

        call      SYM (_Thread_Dispatch)   # invoke Dispatcher

       /*
        * 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
        */

        popa                                # restore general registers
        iret                                # return to interrupted thread

		
SYM (_New_ISR_Handler):	
       /*
        *  Before this was point is reached the vectors unique
        *  entry point did the following:
        *
        *     1. saved sctach 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
        */

        /*
         *  Now switch stacks if necessary
         */

	movw	  SYM (i8259s_cache), ax # move current i8259 interrupt mask in ax
	pushl	  eax			      # push it on the stack
	/*
	 * 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

	/*
	 * acknowledge the interrupt
	 *
	 */
	movb	$PIC_EOI, al
        cmpl    $7, ecx
	jbe	.master
	outb    $PIC_SLAVE_COMMAND_IO_PORT
.master:
        outb    $PIC_MASTER_COMMAND_IO_PORT
	
.check_stack_switch:
	pushl	  ebp
        movl      esp, ebp                  # ebp = previous stack pointer
        cmpl      $0, SYM (_ISR_Nest_level) # is this the outermost interrupt?
        jne       nested                    # No, then continue
        movl      SYM (_CPU_Interrupt_stack_high), esp

        /*
         *  We want to insure that the old stack pointer is on the
         *  stack we will be on at the end of the ISR when we restore it.
         *  By saving it on every interrupt, all we have to do is pop it
         *  near the end of every interrupt.
         */

nested:
        incl      SYM (_ISR_Nest_level)     # one nest level deeper
        incl      SYM (_Thread_Dispatch_disable_level) # disable multitasking
	/*
	 * re-enable interrupts at processor level as the current
	 * interrupt source is now masked via i8259
	 */
	sti
	
        /*
	 *  ECX is preloaded with the vector number but it is a scratch register
	 *  so we must save it again.
	 */
	
        pushl     ecx                       # push vector number
        mov       SYM (current_irq) (,ecx,4),eax
                                            # eax = Users handler
        call      *eax                      # invoke user ISR
	/*
	 * disable interrupts_again
	 */
	cli
        popl       ecx                       # ecx = vector number
	/*
	 * restore stack
	 */
	movl	  ebp, esp
	popl	  ebp

	/*
	 * restore the original i8259 masks
	 */
	popl	  eax
	movw      ax, SYM (i8259s_cache)
	outb	  $PIC_MASTER_IMR_IO_PORT
	movb	  ah, al
	outb	  $PIC_SLAVE_IMR_IO_PORT
	
	
        decl      SYM (_ISR_Nest_level)     # one less ISR nest level
                                            # If interrupts are nested,
                                            #   then dispatching is disabled

        decl      SYM (_Thread_Dispatch_disable_level)
                                            # unnest multitasking
                                            # Is dispatch disabled
        jne       .exit                     # Yes, then exit

        cmpl      $0, SYM (_Context_Switch_necessary)
                                            # Is task switch necessary?
        jne       bframe                    # Yes, then build stack

        cmpl      $0, SYM (_ISR_Signals_to_thread_executing)
                                            # signals sent to Run_thread
                                            #   while in interrupt handler?
        je        .exit                     # No, exit

bframe:
        movl      $0, SYM (_ISR_Signals_to_thread_executing)
	/*
	 * complete code as if a pusha had been executed on entry
	 */
	pushl	  ebx
	pushl	  esp
	pushl	  ebp
	pushl	  esi
	pushl	  edi
                                            # push the isf for Isr_dispatch
        pushl     EFLAGS_OFFSET(esp)        # push tasks eflags
        push      cs                        # cs of Isr_dispatch
        pushl     $ SYM (_New_ISR_Displatch)# entry point
        iret

.exit:
       /*
        * 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 (_New_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)
	
	/*
	 * routine used to initialize the IDT by default
	 */
		
PUBLIC (default_raw_idt_handler)
PUBLIC (raw_idt_notify)
		
SYM (default_raw_idt_handler):
	pusha
	cld
	call	raw_idt_notify
	popa
	iret