Initial checkin of toy OS project.
[os.git] / kernel / hal / intsupport.asm
1 [BITS 32]
2
3 FIRST_EXTERNAL_INT equ 0x30
4 LAST_EXTERNAL_INT equ 0x30+15
5         
6 GLOBAL HalInterruptLoadIDTR
7 GLOBAL HalInterruptGenericHandler
8 GLOBAL HalInterruptHandlerPreamble
9 GLOBAL HalInterruptHandlerPreambleBefore
10 GLOBAL HalInterruptHandlerPreambleAfter
11 GLOBAL HalInterruptInitializePICs
12 GLOBAL HalIoDelay
13         
14 EXTERN g_InterruptHandlerTable
15 EXTERN HalVideoPrint
16 EXTERN HalInterruptSendIrqEoi
17                 
18 ;;  Save registers prior to calling a handler function.
19 ;;  This must be kept up to date with:
20 ;;    - Interrupt_State struct in int.h
21 ;;    - Setup_Initial_Thread_Context() in kthread.c
22 %macro SAVE_REGISTERS 0
23         push eax
24         push ebx
25         push ecx
26         push edx
27         push esi
28         push edi
29         push ebp
30         push ds
31         push es
32         push fs
33         push gs
34 %endmacro
35
36 ;;  Restore registers and clean up the stack after calling a handler function
37 ;;  (i.e., just before we return from the interrupt via an iret instruction).
38 %macro RESTORE_REGISTERS 0
39         pop gs
40         pop fs
41         pop es
42         pop ds
43         pop ebp
44         pop edi
45         pop esi
46         pop edx
47         pop ecx
48         pop ebx
49         pop eax
50         add esp, 8
51 %endmacro
52
53 %macro INT_NO_ERROR 1
54 align 8
55         push 0xffffffff         ; fake the error code
56         push %1                 ; push the int number
57         jmp HalInterruptGenericHandler; 
58 %endmacro
59
60 %macro INT_WITH_ERROR 1
61 align 8
62         push %1                 ; error code already pushed, just push int num
63         jmp HalInterruptGenericHandler
64 %endmacro
65         
66 [SECTION .text]
67 ;;;
68 ;;; void
69 ;;; InterruptLoadIDTR(IDT *p)
70 ;;; 
71 ;;; Function to load the interrupt descriptor table register (IDTR)
72 ;;; callable from C.
73 ;;; 
74 HalInterruptLoadIDTR:
75         mov eax, [esp+4]        ; p
76         lidt [eax]
77         ret
78
79 ;;;
80 ;;; The following is code to initialize the master and slave 8259 
81 ;;; programmable interrupt controllers (PICs).
82 ;;;
83 ;;; For ISA machines there are two 8259's, a master and a slave.  Each has
84 ;;; eight interrupt request (IR) pins and one interrupt (INT) pin.  The
85 ;;; master's INT pin is tied to the INT line of the microprocessor.  The
86 ;;; slave's INT pin is tied to the master's IR2 line.  Thus the master's
87 ;;; IR0, IR1, and IR3..7 are real devices.  But the master's IR2 means
88 ;;; that some IR pin on the slave is asserted.  So the system IRQs are: 
89 ;;;
90 ;;;    (highest default priority)
91 ;;;    IRQ0 = master IR0 [IRQ0 = system timer]
92 ;;;    IRQ1 = master IR1 [IRQ1 = keyboard]
93 ;;;    IRQ2 = master IR2 --> IRQ8..IRQ15 = slave IR0..7
94 ;;;                          [IRQ8 = rt. timer][IRQ13 = coproc.]
95 ;;;    IRQ3 = master IR3
96 ;;;    IRQ4 = master IR4
97 ;;;    IRQ5 = master IR5
98 ;;;    IRQ6 = master IR6
99 ;;;    IRQ7 = master IR7
100 ;;;    (lowest default priority)
101 ;;;
102 ;;; By default these IRQs raise the following interrupt numbers:
103 ;;;
104 ;;;    IRQ0   = int 0x8     [collides with double exception]
105 ;;;    IRQ1   = int 0x9     [collides with coprocessor exception]
106 ;;;    IRQ2/9 = int 0x71
107 ;;;    IRQ3   = int 0xb     [collides with segment not present exception]
108 ;;;    IRQ4   = int 0xc     [collides with stack fault exception]
109 ;;;    IRQ5   = int 0xd     [collides with general protection exception]
110 ;;;    IRQ6   = int 0xe     [collides with page fault]
111 ;;;    IRQ7   = int 0xf
112 ;;;    IRQ8   = int 0x70
113 ;;;    IRQ10  = int 0x72
114 ;;;    IRQ11  = int 0x73
115 ;;;    IRQ12  = int 0x74
116 ;;;    IRQ13  = int 0x75
117 ;;;    IRQ14  = int 0x76
118 ;;;    IRQ15  = int 0x77
119 ;;; 
120 ;;; So the problem is that IRQ0, IRQ1, IRQ3..6 all, by default, map to
121 ;;; interrupt numbers that collide with interrupts raised by the cpu
122 ;;; as a result of a software exception.  The solution to this problem 
123 ;;; is to remap what interrupts are raised by these IRQs by reprogramming
124 ;;; the 8259 PICs to raise new interrupt numbers.
125 ;;;
126 ;;; This is accomplished by sending initialization command words (ICWs)
127 ;;; to I/O ports 0x20-0x21 (master PIC) and 0xA0-0xA1 (slave PIC).  By doing
128 ;;; so we can remap the interrupt numbers raised by IRQs so that:
129 ;;;
130 ;;;    IRQ0   = 0x30       IRQ8  = 0x38
131 ;;;    IRQ1   = 0x31       IRQ10 = 0x3A
132 ;;;    IRQ2/9 = 0x39       IRQ11 = 0x3B
133 ;;;    IRQ3   = 0x33       IRQ12 = 0x3C
134 ;;;    IRQ4   = 0x34       IRQ13 = 0x3D
135 ;;;    IRQ5   = 0x35       IRQ14 = 0x3E
136 ;;;    IRQ6   = 0x36       IRQ15 = 0x3F
137 ;;;    IRQ7   = 0x37
138 ;;;
139 ;;; ICW1 is the same for both the master and slave PIC.  Here's the meaning
140 ;;; of the individual bits in the word:
141 ;;; 
142 ;;;   F  E  D  C  B  A  9  8  |  7  6  5  4  3  2  1  0
143 ;;;              (not used)               |  |  |  |  |__ 1=expect ICW4
144 ;;;                                       |  |  |  |_____ 0=cascade 1=single
145 ;;;                                       |  |  |________ 0=interval-4, 1=8
146 ;;;                                       |  |___________ 0=edge triggered
147 ;;;                                       |               1=level triggered
148 ;;;                                       |______________ must be 1
149 ;;; 
150 ;;;
151 BOTH_ICW1 equ 0x11              ; expect ICW4, cascade mode, call address
152                                 ; interval=8, edge triggered mode
153
154 ;;;
155 ;;; ICW2 tells the PICs their base interrupt number.  For example, the
156 ;;; default base interrupt for the master PIC is 0x8.  Thus IRQ0=0x8,
157 ;;; IRQ1=0x9 ... IRQ7=0xF.  As stated, we want to relocate these.
158 ;;;
159 MASTER_ICW2 equ 0x30            ; use ints 0x30..0x37
160 SLAVE_ICW2 equ 0x38             ; use ints 0x38..0x3F
161
162 ;;;
163 ;;; ICW3 to the master tells it which of its IR pins is connected to the
164 ;;; slave PIC.  ICW3 to the slave tells its id number.
165 ;;;
166 MASTER_ICW3 equ 0x04            ; slave on IR pin 2
167 SLAVE_ICW3 equ 0x02             ; slave id=2
168
169 ;;;
170 ;;; ICW4 to both PICs is another bitvector to set some features:
171 ;;;
172 ;;; 
173 ;;;   F  E  D  C  B  A  9  8  |  7  6  5  4  3  2  1  0
174 ;;;              (not used)               |  ----  |  |__ 0=MCS80/85 1=8086/88 
175 ;;;                                       |    |   |_____ 0=normal 1=auto EOI
176 ;;;                                       |    |_________ 00=non-buf mode
177 ;;;                                       |               10=buf mode, slave
178 ;;;                                       |               11=buf mode, master
179 ;;;                                       |______________ 0=!nested 1=nested
180 ;;; 
181 BOTH_ICW4 equ 0x01
182
183 HalInterruptInitializePICs:
184         mov     al, BOTH_ICW1
185         out     0x20, al            ; ICW1 to master PIC
186         call HalIoDelay
187         out     0xA0, al            ; ICW1 to slave PIC
188         call HalIoDelay
189         
190         mov     al, MASTER_ICW2
191         out     0x21, al            ; ICW2 to master PIC
192         call HalIoDelay
193         mov     al, SLAVE_ICW2      
194         out     0xA1, al            ; ICW2 to slave PIC
195         call HalIoDelay
196         
197         mov     al, MASTER_ICW3
198         out     0x21, al            ; ICW3 to master PIC
199         call HalIoDelay
200         mov     al, SLAVE_ICW3
201         out     0xA1, al            ; ICW3 to slave PIC
202         call HalIoDelay
203         
204         mov     al, BOTH_ICW4
205         out     0x21, al            ; ICW4 to master PIC
206         call HalIoDelay
207         out     0xA1, al            ; ICW4 to slave PIC
208         call HalIoDelay
209
210 ;;;
211 ;;; After the four ICW sequence has been completed any subsequent writes
212 ;;; to port 0x21 or 0xA1 set the IRQ mask for the master/slave PIC.  If
213 ;;; this mask is 0xFF all IRQs are disabled.  If this mask is 0x00
214 ;;; all IRQs are enabled.
215 ;;; 
216         mov     al, 0xff            ; slave PIC cannot interrupt
217         out     0xA1, al
218         call HalIoDelay
219         mov     al, 0xfb            ; mask all IRQs but 2 (the slave PIC) in master
220         out     0x21, al
221         call HalIoDelay
222         ret
223
224 ;;;
225 ;;; When doing I/O operations this routine can be used to "delay" long
226 ;;; enough to let the external controller keep up with the cpu.
227 ;;;
228 HalIoDelay:
229         jmp     .done
230 .done:  ret
231
232 ;;;
233 ;;; This is the start of the main interrupt handler code.  The first section
234 ;;; of this code is called the InterruptHandlerPreamble.  It consists of
235 ;;; 256 entry points in a table (one per possible interrupt number).  These
236 ;;; preamble entry points push the interrupt number and (when needed for
237 ;;; interrupt numbers that the processor doesn't automatically push an error
238 ;;; code for) a fake error code.  Then they jump to the main generic interrupt
239 ;;; handling routing: InterruptGenericHandler.  Thus the stack is always layed
240 ;;; out in the same way when control passes to the generic handler:
241 ;;;
242 ;;;          saved ss             (pushed by cpu for system calls)
243 ;;;          saved esp            (pushed by cpu for system calls)
244 ;;;          ------------
245 ;;;          saved eflags         (pushed by cpu)
246 ;;;          saved CS             (pushed by cpu)
247 ;;;          saved IP             (pushed by cpu)
248 ;;;          error code           (pushed by preamble or cpu)
249 ;;;          interrupt number     (pushed by preamble)
250 ;;; esp -->  return address       (pushed by jmp InteruptGenericHandler)
251 ;;;
252 ;;; The first thing InterruptGenericHandler does is push the rest of the
253 ;;; registers onto the stack.
254 ;;; 
255 align 8
256 HalInterruptHandlerPreamble:
257 HalInterruptHandlerPreambleBefore: 
258         INT_NO_ERROR    0
259         align 8
260 HalInterruptHandlerPreambleAfter:  
261         INT_NO_ERROR    1
262         INT_NO_ERROR    2
263         INT_NO_ERROR    3
264         INT_NO_ERROR    4
265         INT_NO_ERROR    5
266         INT_NO_ERROR    6
267         INT_NO_ERROR    7
268         INT_WITH_ERROR  8
269         INT_NO_ERROR    9
270         INT_WITH_ERROR  10
271         INT_WITH_ERROR  11
272         INT_WITH_ERROR  12
273         INT_WITH_ERROR  13
274         INT_WITH_ERROR  14
275         INT_NO_ERROR    15
276         INT_NO_ERROR    16
277         INT_WITH_ERROR  17
278
279 ;;;
280 ;;; The rest of the interrupt numbers are INT_NO_ERRORs.  Use nasm's
281 ;;; %rep command to do them all at once.
282 ;;; 
283 %assign intNum 18
284 %rep (256 - 18)
285         INT_NO_ERROR    intNum
286 %assign intNum intNum + 1
287 %endrep
288     
289 ;;;
290 ;;; This is the generic interrupt handler which is called by the preambles
291 ;;; above.  It's job is to save the registers on the stack and then transfer
292 ;;; control to a specific per-interrupt handler.  It finds the address of
293 ;;; the per-interrupt handler to call by indexing into g_InterruptHandlerTable
294 ;;; 
295 align 8
296 HalInterruptGenericHandler:
297         SAVE_REGISTERS
298
299         ;; Ensure that we're using the kernel data segment
300         mov ax, (2<<3)
301         mov ds, ax
302         mov es, ax
303         mov fs, ax
304         mov gs, ax
305
306         ;; Get interrupt number the preamble pushed for us
307         mov esi, [esp+44]
308
309         ;; Get the address of the handler function from the
310         ;; table of handler functions.
311         mov eax, g_InterruptHandlerTable
312         mov ebx, [eax+esi*4]
313
314         ;; Call the handler.  The argument passed is a pointer to
315         ;; INTERRUPT_STATE.  Interrupts are enabled at this point
316         ;; so if the handler is non-reentrant it must disable them.
317         push esp
318         call ebx
319         add esp, 4
320
321         push esp
322         call HalInterruptSendIrqEoi
323         add esp, 4
324
325         RESTORE_REGISTERS
326         iret