3 FIRST_EXTERNAL_INT equ 0x30
4 LAST_EXTERNAL_INT equ 0x30+15
6 GLOBAL HalInterruptLoadIDTR
7 GLOBAL HalInterruptGenericHandler
8 GLOBAL HalInterruptHandlerPreamble
9 GLOBAL HalInterruptHandlerPreambleBefore
10 GLOBAL HalInterruptHandlerPreambleAfter
11 GLOBAL HalInterruptInitializePICs
14 EXTERN g_InterruptHandlerTable
16 EXTERN HalInterruptSendIrqEoi
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
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
55 push 0xffffffff ; fake the error code
56 push %1 ; push the int number
57 jmp HalInterruptGenericHandler;
60 %macro INT_WITH_ERROR 1
62 push %1 ; error code already pushed, just push int num
63 jmp HalInterruptGenericHandler
69 ;;; InterruptLoadIDTR(IDT *p)
71 ;;; Function to load the interrupt descriptor table register (IDTR)
80 ;;; The following is code to initialize the master and slave 8259
81 ;;; programmable interrupt controllers (PICs).
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:
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.]
100 ;;; (lowest default priority)
102 ;;; By default these IRQs raise the following interrupt numbers:
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]
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.
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:
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
139 ;;; ICW1 is the same for both the master and slave PIC. Here's the meaning
140 ;;; of the individual bits in the word:
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
151 BOTH_ICW1 equ 0x11 ; expect ICW4, cascade mode, call address
152 ; interval=8, edge triggered mode
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.
159 MASTER_ICW2 equ 0x30 ; use ints 0x30..0x37
160 SLAVE_ICW2 equ 0x38 ; use ints 0x38..0x3F
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.
166 MASTER_ICW3 equ 0x04 ; slave on IR pin 2
167 SLAVE_ICW3 equ 0x02 ; slave id=2
170 ;;; ICW4 to both PICs is another bitvector to set some features:
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
183 HalInterruptInitializePICs:
185 out 0x20, al ; ICW1 to master PIC
187 out 0xA0, al ; ICW1 to slave PIC
191 out 0x21, al ; ICW2 to master PIC
194 out 0xA1, al ; ICW2 to slave PIC
198 out 0x21, al ; ICW3 to master PIC
201 out 0xA1, al ; ICW3 to slave PIC
205 out 0x21, al ; ICW4 to master PIC
207 out 0xA1, al ; ICW4 to slave PIC
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.
216 mov al, 0xff ; slave PIC cannot interrupt
219 mov al, 0xfb ; mask all IRQs but 2 (the slave PIC) in master
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.
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:
242 ;;; saved ss (pushed by cpu for system calls)
243 ;;; saved esp (pushed by cpu for system calls)
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)
252 ;;; The first thing InterruptGenericHandler does is push the rest of the
253 ;;; registers onto the stack.
256 HalInterruptHandlerPreamble:
257 HalInterruptHandlerPreambleBefore:
260 HalInterruptHandlerPreambleAfter:
280 ;;; The rest of the interrupt numbers are INT_NO_ERRORs. Use nasm's
281 ;;; %rep command to do them all at once.
286 %assign intNum intNum + 1
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
296 HalInterruptGenericHandler:
299 ;; Ensure that we're using the kernel data segment
306 ;; Get interrupt number the preamble pushed for us
309 ;; Get the address of the handler function from the
310 ;; table of handler functions.
311 mov eax, g_InterruptHandlerTable
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.
322 call HalInterruptSendIrqEoi