branch update for HEAD-2003021201
[reactos.git] / hal / halx86 / irql.c
1 /* $Id$
2  *
3  * COPYRIGHT:       See COPYING in the top level directory
4  * PROJECT:         ReactOS kernel
5  * FILE:            ntoskrnl/hal/x86/irql.c
6  * PURPOSE:         Implements IRQLs
7  * PROGRAMMER:      David Welch (welch@cwcom.net)
8  */
9
10 /* INCLUDES *****************************************************************/
11
12 #include <ddk/ntddk.h>
13 #include <internal/ke.h>
14 #include <internal/ps.h>
15 #include <ntos/minmax.h>
16
17 #define NDEBUG
18 #include <internal/debug.h>
19
20 /* GLOBALS ******************************************************************/
21
22 #define NR_IRQS         (16)
23 #define IRQ_BASE        (0x40)
24
25 /*
26  * PURPOSE: Current irq level
27  */
28 static KIRQL CurrentIrql = HIGH_LEVEL;
29
30 #ifndef LIBCAPTIVE
31
32 typedef union
33 {
34    USHORT both;
35    struct
36    {
37       BYTE master;
38       BYTE slave;
39    };
40 }
41 PIC_MASK;
42    
43 /* 
44  * PURPOSE: - Mask for HalEnableSystemInterrupt and HalDisableSystemInterrupt
45  *          - At startup enable timer and cascade 
46  */
47 static PIC_MASK pic_mask = {.both = 0xFFFA};
48
49
50 /*
51  * PURPOSE: Mask for disabling of acknowledged interrupts 
52  */
53 static PIC_MASK pic_mask_intr = {.both = 0x0000};
54
55 extern IMPORTED ULONG DpcQueueSize;
56
57 static ULONG HalpPendingInterruptCount[NR_IRQS];
58
59 #define DIRQL_TO_IRQ(x)  (PROFILE_LEVEL - x)
60 #define IRQ_TO_DIRQL(x)  (PROFILE_LEVEL - x)
61
62 VOID STDCALL
63 KiInterruptDispatch2 (ULONG Irq, KIRQL old_level);
64
65 #endif /* LIBCAPTIVE */
66
67 /* FUNCTIONS ****************************************************************/
68
69 KIRQL STDCALL KeGetCurrentIrql (VOID)
70 /*
71  * PURPOSE: Returns the current irq level
72  * RETURNS: The current irq level
73  */
74 {
75   return(CurrentIrql);
76 }
77
78 #ifndef LIBCAPTIVE
79
80 VOID HalpInitPICs(VOID)
81 {
82   memset(HalpPendingInterruptCount, 0, sizeof(HalpPendingInterruptCount));
83
84   /* Initialization sequence */
85   WRITE_PORT_UCHAR((PUCHAR)0x20, 0x11);
86   WRITE_PORT_UCHAR((PUCHAR)0xa0, 0x11);
87   /* Start of hardware irqs (0x24) */
88   WRITE_PORT_UCHAR((PUCHAR)0x21, 0x40);
89   WRITE_PORT_UCHAR((PUCHAR)0xa1, 0x48);
90   /* 8259-1 is master */
91   WRITE_PORT_UCHAR((PUCHAR)0x21, 0x4);
92   /* 8259-2 is slave */
93   WRITE_PORT_UCHAR((PUCHAR)0xa1, 0x2);
94   /* 8086 mode */
95   WRITE_PORT_UCHAR((PUCHAR)0x21, 0x1);
96   WRITE_PORT_UCHAR((PUCHAR)0xa1, 0x1);   
97   /* Enable interrupts */
98   WRITE_PORT_UCHAR((PUCHAR)0x21, pic_mask.master);
99   WRITE_PORT_UCHAR((PUCHAR)0xa1, pic_mask.slave);
100   
101   /* We can now enable interrupts */
102   __asm__ __volatile__ ("sti\n\t");
103 }
104
105 VOID HalpEndSystemInterrupt(KIRQL Irql)
106 /*
107  * FUNCTION: Enable all irqs with higher priority.
108  */
109 {
110   const USHORT mask[] = 
111   {
112      0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
113      0x0000, 0x0000, 0x0000, 0x0000, 0x8000, 0xc000, 0xe000, 0xf000,
114      0xf800, 0xfc00, 0xfe00, 0xff00, 0xff80, 0xffc0, 0xffe0, 0xfff0,
115      0xfff8, 0xfffc, 0xfffe, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
116   };     
117
118   /* Interrupts should be disable while enabling irqs of both pics */
119   __asm__("pushf\n\t");
120   __asm__("cli\n\t");
121   pic_mask_intr.both &= mask[Irql];
122   WRITE_PORT_UCHAR((PUCHAR)0x21, pic_mask.master|pic_mask_intr.master);
123   WRITE_PORT_UCHAR((PUCHAR)0xa1, pic_mask.slave|pic_mask_intr.slave);
124   __asm__("popf\n\t");
125 }
126
127 VOID STATIC
128 HalpExecuteIrqs(KIRQL NewIrql)
129 {
130   ULONG IrqLimit, i;
131   
132   IrqLimit = min(PROFILE_LEVEL - NewIrql, NR_IRQS);
133
134   /*
135    * For each irq if there have been any deferred interrupts then now
136    * dispatch them.
137    */
138   for (i = 0; i < IrqLimit; i++)
139     {
140       if (HalpPendingInterruptCount[i] > 0)
141         {
142            CurrentIrql = IRQ_TO_DIRQL(i);
143
144            while (HalpPendingInterruptCount[i] > 0)
145              {
146                /*
147                 * For each deferred interrupt execute all the handlers at DIRQL.
148                 */
149                KiInterruptDispatch2(i, NewIrql);
150                HalpPendingInterruptCount[i]--;
151              }
152            CurrentIrql--;
153            HalpEndSystemInterrupt(CurrentIrql);
154         }
155     }
156
157 }
158
159 VOID STATIC
160 HalpLowerIrql(KIRQL NewIrql)
161 {
162   if (NewIrql >= PROFILE_LEVEL)
163     {
164       CurrentIrql = NewIrql;
165       return;
166     }
167   HalpExecuteIrqs(NewIrql);
168   if (NewIrql >= DISPATCH_LEVEL)
169     {
170       CurrentIrql = NewIrql;
171       return;
172     }
173   CurrentIrql = DISPATCH_LEVEL;
174   if (DpcQueueSize > 0)
175     {
176       KiDispatchInterrupt();
177     }
178   CurrentIrql = APC_LEVEL;
179   if (NewIrql == APC_LEVEL)
180     {
181       return;
182     }
183   if (KeGetCurrentThread() != NULL && 
184       KeGetCurrentThread()->ApcState.KernelApcPending)
185     {
186       KiDeliverApc(0, 0, 0);
187     }
188   CurrentIrql = PASSIVE_LEVEL;
189 }
190
191 #endif /* LIBCAPTIVE */
192
193 /**********************************************************************
194  * NAME                                                 EXPORTED
195  *      KfLowerIrql
196  *
197  * DESCRIPTION
198  *      Restores the irq level on the current processor
199  *
200  * ARGUMENTS
201  *      NewIrql = Irql to lower to
202  *
203  * RETURN VALUE
204  *      None
205  *
206  * NOTES
207  *      Uses fastcall convention
208  */
209 VOID FASTCALL
210 KfLowerIrql (KIRQL      NewIrql)
211 {
212   KIRQL OldIrql;
213   
214   DPRINT("KfLowerIrql(NewIrql %d)\n", NewIrql);
215   
216   if (NewIrql > CurrentIrql)
217     {
218       DbgPrint ("(%s:%d) NewIrql %x CurrentIrql %x\n",
219                 __FILE__, __LINE__, NewIrql, CurrentIrql);
220       KeBugCheck(0);
221       for(;;);
222     }
223   
224 #ifndef LIBCAPTIVE
225   HalpLowerIrql(NewIrql);
226 #else /* LIBCAPTIVE */
227   CurrentIrql = NewIrql;
228 #endif /* LIBCAPTIVE */
229 }
230
231
232 /**********************************************************************
233  * NAME                                                 EXPORTED
234  *      KeLowerIrql
235  *
236  * DESCRIPTION
237  *      Restores the irq level on the current processor
238  *
239  * ARGUMENTS
240  *      NewIrql = Irql to lower to
241  *
242  * RETURN VALUE
243  *      None
244  *
245  * NOTES
246  */
247
248 VOID STDCALL
249 KeLowerIrql (KIRQL NewIrql)
250 {
251   KfLowerIrql (NewIrql);
252 }
253
254
255 /**********************************************************************
256  * NAME                                                 EXPORTED
257  *      KfRaiseIrql
258  *
259  * DESCRIPTION
260  *      Raises the hardware priority (irql)
261  *
262  * ARGUMENTS
263  *      NewIrql = Irql to raise to
264  *
265  * RETURN VALUE
266  *      previous irq level
267  *
268  * NOTES
269  *      Uses fastcall convention
270  */
271
272 KIRQL FASTCALL
273 KfRaiseIrql (KIRQL      NewIrql)
274 {
275   KIRQL OldIrql;
276   
277   DPRINT("KfRaiseIrql(NewIrql %d)\n", NewIrql);
278   
279   if (NewIrql < CurrentIrql)
280     {
281       DbgPrint ("%s:%d CurrentIrql %x NewIrql %x\n",
282                 __FILE__,__LINE__,CurrentIrql,NewIrql);
283       KeBugCheck (0);
284       for(;;);
285     }
286   
287   OldIrql = CurrentIrql;
288   CurrentIrql = NewIrql;
289   return OldIrql;
290 }
291
292
293 /**********************************************************************
294  * NAME                                                 EXPORTED
295  *      KeRaiseIrql
296  *
297  * DESCRIPTION
298  *      Raises the hardware priority (irql)
299  *
300  * ARGUMENTS
301  *      NewIrql = Irql to raise to
302  *      OldIrql (OUT) = Caller supplied storage for the previous irql
303  *
304  * RETURN VALUE
305  *      None
306  *
307  * NOTES
308  *      Calls KfRaiseIrql
309  */
310 VOID STDCALL
311 KeRaiseIrql (KIRQL      NewIrql,
312              PKIRQL     OldIrql)
313 {
314   *OldIrql = KfRaiseIrql (NewIrql);
315 }
316
317 #ifndef LIBCAPTIVE
318
319 /**********************************************************************
320  * NAME                                                 EXPORTED
321  *      KeRaiseIrqlToDpcLevel
322  *
323  * DESCRIPTION
324  *      Raises the hardware priority (irql) to DISPATCH level
325  *
326  * ARGUMENTS
327  *      None
328  *
329  * RETURN VALUE
330  *      Previous irq level
331  *
332  * NOTES
333  *      Calls KfRaiseIrql
334  */
335
336 KIRQL STDCALL
337 KeRaiseIrqlToDpcLevel (VOID)
338 {
339   return KfRaiseIrql (DISPATCH_LEVEL);
340 }
341
342
343 /**********************************************************************
344  * NAME                                                 EXPORTED
345  *      KeRaiseIrqlToSynchLevel
346  *
347  * DESCRIPTION
348  *      Raises the hardware priority (irql) to CLOCK2 level
349  *
350  * ARGUMENTS
351  *      None
352  *
353  * RETURN VALUE
354  *      Previous irq level
355  *
356  * NOTES
357  *      Calls KfRaiseIrql
358  */
359
360 KIRQL STDCALL
361 KeRaiseIrqlToSynchLevel (VOID)
362 {
363   return KfRaiseIrql (CLOCK2_LEVEL);
364 }
365
366
367 BOOLEAN STDCALL 
368 HalBeginSystemInterrupt (ULONG Vector,
369                          KIRQL Irql,
370                          PKIRQL OldIrql)
371 {
372   ULONG irq;
373   if (Vector < IRQ_BASE || Vector >= IRQ_BASE + NR_IRQS)
374     {
375       return(FALSE);
376     }
377   irq = Vector - IRQ_BASE;
378   pic_mask_intr.both |= ((1 << irq) & 0xfffe);  // do not disable the timer interrupt
379
380   if (irq < 8)
381   {
382      WRITE_PORT_UCHAR((PUCHAR)0x21, pic_mask.master|pic_mask_intr.master);
383      WRITE_PORT_UCHAR((PUCHAR)0x20, 0x20);
384   }
385   else
386   {
387      WRITE_PORT_UCHAR((PUCHAR)0xa1, pic_mask.slave|pic_mask_intr.slave);
388      /* Send EOI to the PICs */
389      WRITE_PORT_UCHAR((PUCHAR)0x20,0x20);
390      WRITE_PORT_UCHAR((PUCHAR)0xa0,0x20);
391   }
392   
393   if (CurrentIrql >= Irql)
394     {
395       HalpPendingInterruptCount[irq]++;
396       return(FALSE);
397     }
398   *OldIrql = CurrentIrql;
399   CurrentIrql = Irql;
400
401   return(TRUE);
402 }
403
404
405 VOID STDCALL HalEndSystemInterrupt (KIRQL Irql, ULONG Unknown2)
406 /*
407  * FUNCTION: Finish a system interrupt and restore the specified irq level.
408  */
409 {
410   HalpLowerIrql(Irql);
411   HalpEndSystemInterrupt(Irql);
412 }
413   
414 BOOLEAN STDCALL HalDisableSystemInterrupt (ULONG Vector,
415                                            ULONG Unknown2)
416 {
417   ULONG irq;
418   
419   if (Vector < IRQ_BASE || Vector >= IRQ_BASE + NR_IRQS)
420     return FALSE;
421
422   irq = Vector - IRQ_BASE;
423   pic_mask.both |= (1 << irq);
424   if (irq < 8)
425      {
426       WRITE_PORT_UCHAR((PUCHAR)0x21, pic_mask.master|pic_mask_intr.slave);
427      }
428   else
429     {
430       WRITE_PORT_UCHAR((PUCHAR)0xa1, pic_mask.slave|pic_mask_intr.slave);
431     }
432   
433   return TRUE;
434 }
435
436
437 BOOLEAN STDCALL HalEnableSystemInterrupt (ULONG Vector,
438                                           ULONG Unknown2,
439                                           ULONG Unknown3)
440 {
441   ULONG irq;
442
443   if (Vector < IRQ_BASE || Vector >= IRQ_BASE + NR_IRQS)
444     return FALSE;
445
446   irq = Vector - IRQ_BASE;
447   pic_mask.both &= ~(1 << irq);
448   if (irq < 8)
449     {
450       WRITE_PORT_UCHAR((PUCHAR)0x21, pic_mask.master|pic_mask_intr.master);
451     }
452   else
453      {
454        WRITE_PORT_UCHAR((PUCHAR)0xa1, pic_mask.slave|pic_mask_intr.slave);
455      }
456
457   return TRUE;
458 }
459
460 #endif /* LIBCAPTIVE */
461
462 /* EOF */