xref: /reactos/hal/halx86/apic/apic.c (revision 23373acb)
1 /*
2  * PROJECT:         ReactOS HAL
3  * LICENSE:         GNU GPL - See COPYING in the top level directory
4  * FILE:            hal/halx86/apic/apic.c
5  * PURPOSE:         HAL APIC Management and Control Code
6  * PROGRAMMERS:     Timo Kreuzer (timo.kreuzer@reactos.org)
7  * REFERENCES:      http://www.joseflores.com/docs/ExploringIrql.html
8  *                  http://www.codeproject.com/KB/system/soviet_kernel_hack.aspx
9  *                  http://bbs.unixmap.net/thread-2022-1-1.html
10  */
11 
12 /* INCLUDES *******************************************************************/
13 
14 #include <hal.h>
15 #define NDEBUG
16 #include <debug.h>
17 
18 #include "apic.h"
19 void __cdecl HackEoi(void);
20 
21 #ifndef _M_AMD64
22 #define APIC_LAZY_IRQL
23 #endif
24 
25 /* GLOBALS ********************************************************************/
26 
27 ULONG ApicVersion;
28 UCHAR HalpVectorToIndex[256];
29 
30 #ifndef _M_AMD64
31 const UCHAR
32 HalpIRQLtoTPR[32] =
33 {
34     0x00, /*  0 PASSIVE_LEVEL */
35     0x3d, /*  1 APC_LEVEL */
36     0x41, /*  2 DISPATCH_LEVEL */
37     0x41, /*  3 \  */
38     0x51, /*  4  \ */
39     0x61, /*  5  | */
40     0x71, /*  6  | */
41     0x81, /*  7  | */
42     0x91, /*  8  | */
43     0xa1, /*  9  | */
44     0xb1, /* 10  | */
45     0xb1, /* 11  | */
46     0xb1, /* 12  | */
47     0xb1, /* 13  | */
48     0xb1, /* 14  | */
49     0xb1, /* 15 DEVICE IRQL */
50     0xb1, /* 16  | */
51     0xb1, /* 17  | */
52     0xb1, /* 18  | */
53     0xb1, /* 19  | */
54     0xb1, /* 20  | */
55     0xb1, /* 21  | */
56     0xb1, /* 22  | */
57     0xb1, /* 23  | */
58     0xb1, /* 24  | */
59     0xb1, /* 25  / */
60     0xb1, /* 26 /  */
61     0xc1, /* 27 PROFILE_LEVEL */
62     0xd1, /* 28 CLOCK2_LEVEL */
63     0xe1, /* 29 IPI_LEVEL */
64     0xef, /* 30 POWER_LEVEL */
65     0xff, /* 31 HIGH_LEVEL */
66 };
67 
68 const KIRQL
69 HalVectorToIRQL[16] =
70 {
71        0, /* 00 PASSIVE_LEVEL */
72     0xff, /* 10 */
73     0xff, /* 20 */
74        1, /* 3D APC_LEVEL */
75        2, /* 41 DISPATCH_LEVEL */
76        4, /* 50 \ */
77        5, /* 60  \ */
78        6, /* 70  | */
79        7, /* 80 DEVICE IRQL */
80        8, /* 90  | */
81        9, /* A0  / */
82       10, /* B0 /  */
83       27, /* C1 PROFILE_LEVEL */
84       28, /* D1 CLOCK2_LEVEL */
85       29, /* E1 IPI_LEVEL / EF POWER_LEVEL */
86       31, /* FF HIGH_LEVEL */
87 };
88 #endif
89 
90 /* PRIVATE FUNCTIONS **********************************************************/
91 
92 FORCEINLINE
93 ULONG
94 IOApicRead(UCHAR Register)
95 {
96     /* Select the register, then do the read */
97     *(volatile UCHAR *)(IOAPIC_BASE + IOAPIC_IOREGSEL) = Register;
98     return *(volatile ULONG *)(IOAPIC_BASE + IOAPIC_IOWIN);
99 }
100 
101 FORCEINLINE
102 VOID
103 IOApicWrite(UCHAR Register, ULONG Value)
104 {
105     /* Select the register, then do the write */
106     *(volatile UCHAR *)(IOAPIC_BASE + IOAPIC_IOREGSEL) = Register;
107     *(volatile ULONG *)(IOAPIC_BASE + IOAPIC_IOWIN) = Value;
108 }
109 
110 FORCEINLINE
111 VOID
112 ApicWriteIORedirectionEntry(
113     UCHAR Index,
114     IOAPIC_REDIRECTION_REGISTER ReDirReg)
115 {
116     IOApicWrite(IOAPIC_REDTBL + 2 * Index, ReDirReg.Long0);
117     IOApicWrite(IOAPIC_REDTBL + 2 * Index + 1, ReDirReg.Long1);
118 }
119 
120 FORCEINLINE
121 IOAPIC_REDIRECTION_REGISTER
122 ApicReadIORedirectionEntry(
123     UCHAR Index)
124 {
125     IOAPIC_REDIRECTION_REGISTER ReDirReg;
126 
127     ReDirReg.Long0 = IOApicRead(IOAPIC_REDTBL + 2 * Index);
128     ReDirReg.Long1 = IOApicRead(IOAPIC_REDTBL + 2 * Index + 1);
129 
130     return ReDirReg;
131 }
132 
133 FORCEINLINE
134 VOID
135 ApicRequestInterrupt(IN UCHAR Vector, UCHAR TriggerMode)
136 {
137     APIC_COMMAND_REGISTER CommandRegister;
138 
139     /* Setup the command register */
140     CommandRegister.Long0 = 0;
141     CommandRegister.Vector = Vector;
142     CommandRegister.MessageType = APIC_MT_Fixed;
143     CommandRegister.TriggerMode = TriggerMode;
144     CommandRegister.DestinationShortHand = APIC_DSH_Self;
145 
146     /* Write the low dword to send the interrupt */
147     ApicWrite(APIC_ICR0, CommandRegister.Long0);
148 }
149 
150 FORCEINLINE
151 VOID
152 ApicSendEOI(void)
153 {
154     //ApicWrite(APIC_EOI, 0);
155     HackEoi();
156 }
157 
158 FORCEINLINE
159 KIRQL
160 ApicGetProcessorIrql(VOID)
161 {
162     /* Read the TPR and convert it to an IRQL */
163     return TprToIrql(ApicRead(APIC_PPR));
164 }
165 
166 FORCEINLINE
167 KIRQL
168 ApicGetCurrentIrql(VOID)
169 {
170 #ifdef _M_AMD64
171     return (KIRQL)__readcr8();
172 #elif defined(APIC_LAZY_IRQL)
173     // HACK: some magic to Sync VBox's APIC registers
174     ApicRead(APIC_VER);
175 
176     /* Return the field in the PCR */
177     return (KIRQL)__readfsbyte(FIELD_OFFSET(KPCR, Irql));
178 #else
179     // HACK: some magic to Sync VBox's APIC registers
180     ApicRead(APIC_VER);
181 
182     /* Read the TPR and convert it to an IRQL */
183     return TprToIrql(ApicRead(APIC_TPR));
184 #endif
185 }
186 
187 FORCEINLINE
188 VOID
189 ApicSetIrql(KIRQL Irql)
190 {
191 #ifdef _M_AMD64
192     __writecr8(Irql);
193 #elif defined(APIC_LAZY_IRQL)
194     __writefsbyte(FIELD_OFFSET(KPCR, Irql), Irql);
195 #else
196     /* Convert IRQL and write the TPR */
197     ApicWrite(APIC_TPR, IrqlToTpr(Irql));
198 #endif
199 }
200 #define ApicRaiseIrql ApicSetIrql
201 
202 #ifdef APIC_LAZY_IRQL
203 FORCEINLINE
204 VOID
205 ApicLowerIrql(KIRQL Irql)
206 {
207     __writefsbyte(FIELD_OFFSET(KPCR, Irql), Irql);
208 
209     /* Is the new Irql lower than set in the TPR? */
210     if (Irql < KeGetPcr()->IRR)
211     {
212         /* Save the new hard IRQL in the IRR field */
213         KeGetPcr()->IRR = Irql;
214 
215         /* Need to lower it back */
216         ApicWrite(APIC_TPR, IrqlToTpr(Irql));
217     }
218 }
219 #else
220 #define ApicLowerIrql ApicSetIrql
221 #endif
222 
223 UCHAR
224 FASTCALL
225 HalpIrqToVector(UCHAR Irq)
226 {
227     IOAPIC_REDIRECTION_REGISTER ReDirReg;
228 
229     /* Read low dword of the redirection entry */
230     ReDirReg.Long0 = IOApicRead(IOAPIC_REDTBL + 2 * Irq);
231 
232     /* Return the vector */
233     return (UCHAR)ReDirReg.Vector;
234 }
235 
236 KIRQL
237 FASTCALL
238 HalpVectorToIrql(UCHAR Vector)
239 {
240     return TprToIrql(Vector);
241 }
242 
243 UCHAR
244 FASTCALL
245 HalpVectorToIrq(UCHAR Vector)
246 {
247     return HalpVectorToIndex[Vector];
248 }
249 
250 VOID
251 NTAPI
252 HalpSendEOI(VOID)
253 {
254     ApicSendEOI();
255 }
256 
257 VOID
258 NTAPI
259 ApicInitializeLocalApic(ULONG Cpu)
260 {
261     APIC_BASE_ADRESS_REGISTER BaseRegister;
262     APIC_SPURIOUS_INERRUPT_REGISTER SpIntRegister;
263     LVT_REGISTER LvtEntry;
264 
265     /* Enable the APIC if it wasn't yet */
266     BaseRegister.Long = __readmsr(MSR_APIC_BASE);
267     BaseRegister.Enable = 1;
268     BaseRegister.BootStrapCPUCore = (Cpu == 0);
269     __writemsr(MSR_APIC_BASE, BaseRegister.Long);
270 
271     /* Set spurious vector and SoftwareEnable to 1 */
272     SpIntRegister.Long = ApicRead(APIC_SIVR);
273     SpIntRegister.Vector = APIC_SPURIOUS_VECTOR;
274     SpIntRegister.SoftwareEnable = 1;
275     SpIntRegister.FocusCPUCoreChecking = 0;
276     ApicWrite(APIC_SIVR, SpIntRegister.Long);
277 
278     /* Read the version and save it globally */
279     if (Cpu == 0) ApicVersion = ApicRead(APIC_VER);
280 
281     /* Set the mode to flat (max 8 CPUs supported!) */
282     ApicWrite(APIC_DFR, APIC_DF_Flat);
283 
284     /* Set logical apic ID */
285     ApicWrite(APIC_LDR, ApicLogicalId(Cpu) << 24);
286 
287     /* Set the spurious ISR */
288     KeRegisterInterruptHandler(APIC_SPURIOUS_VECTOR, ApicSpuriousService);
289 
290     /* Create a template LVT */
291     LvtEntry.Long = 0;
292     LvtEntry.Vector = 0xFF;
293     LvtEntry.MessageType = APIC_MT_Fixed;
294     LvtEntry.DeliveryStatus = 0;
295     LvtEntry.RemoteIRR = 0;
296     LvtEntry.TriggerMode = APIC_TGM_Edge;
297     LvtEntry.Mask = 1;
298     LvtEntry.TimerMode = 0;
299 
300     /* Initialize and mask LVTs */
301     ApicWrite(APIC_TMRLVTR, LvtEntry.Long);
302     ApicWrite(APIC_THRMLVTR, LvtEntry.Long);
303     ApicWrite(APIC_PCLVTR, LvtEntry.Long);
304     ApicWrite(APIC_EXT0LVTR, LvtEntry.Long);
305     ApicWrite(APIC_EXT1LVTR, LvtEntry.Long);
306     ApicWrite(APIC_EXT2LVTR, LvtEntry.Long);
307     ApicWrite(APIC_EXT3LVTR, LvtEntry.Long);
308 
309     /* LINT0 */
310     LvtEntry.Vector = APIC_SPURIOUS_VECTOR;
311     LvtEntry.MessageType = APIC_MT_ExtInt;
312     ApicWrite(APIC_LINT0, LvtEntry.Long);
313 
314     /* Enable LINT1 (NMI) */
315     LvtEntry.Mask = 0;
316     LvtEntry.Vector = APIC_NMI_VECTOR;
317     LvtEntry.MessageType = APIC_MT_NMI;
318     LvtEntry.TriggerMode = APIC_TGM_Level;
319     ApicWrite(APIC_LINT1, LvtEntry.Long);
320 
321     /* Enable error LVTR */
322     LvtEntry.Vector = APIC_ERROR_VECTOR;
323     LvtEntry.MessageType = APIC_MT_Fixed;
324     ApicWrite(APIC_ERRLVTR, LvtEntry.Long);
325 
326     /* Set the IRQL from the PCR */
327     ApicSetIrql(KeGetPcr()->Irql);
328 #ifdef APIC_LAZY_IRQL
329     /* Save the new hard IRQL in the IRR field */
330     KeGetPcr()->IRR = KeGetPcr()->Irql;
331 #endif
332 }
333 
334 UCHAR
335 NTAPI
336 HalpAllocateSystemInterrupt(
337     IN UCHAR Irq,
338     IN KIRQL Irql)
339 {
340     IOAPIC_REDIRECTION_REGISTER ReDirReg;
341     IN UCHAR Vector;
342 
343     /* Start with lowest vector */
344     Vector = IrqlToTpr(Irql) & 0xF0;
345 
346     /* Find an empty vector */
347     while (HalpVectorToIndex[Vector] != 0xFF)
348     {
349         Vector++;
350 
351         /* Check if we went over the edge */
352         if (TprToIrql(Vector) > Irql)
353         {
354             /* Nothing free, return failure */
355             return 0;
356         }
357     }
358 
359     /* Save irq in the table */
360     HalpVectorToIndex[Vector] = Irq;
361 
362     /* Setup a redirection entry */
363     ReDirReg.Vector = Vector;
364     ReDirReg.DeliveryMode = APIC_MT_LowestPriority;
365     ReDirReg.DestinationMode = APIC_DM_Logical;
366     ReDirReg.DeliveryStatus = 0;
367     ReDirReg.Polarity = 0;
368     ReDirReg.RemoteIRR = 0;
369     ReDirReg.TriggerMode = APIC_TGM_Edge;
370     ReDirReg.Mask = 1;
371     ReDirReg.Reserved = 0;
372     ReDirReg.Destination = 0;
373 
374     /* Initialize entry */
375     IOApicWrite(IOAPIC_REDTBL + 2 * Irq, ReDirReg.Long0);
376     IOApicWrite(IOAPIC_REDTBL + 2 * Irq + 1, ReDirReg.Long1);
377 
378     return Vector;
379 }
380 
381 VOID
382 NTAPI
383 ApicInitializeIOApic(VOID)
384 {
385     PHARDWARE_PTE Pte;
386     IOAPIC_REDIRECTION_REGISTER ReDirReg;
387     UCHAR Index;
388     ULONG Vector;
389 
390     /* Map the I/O Apic page */
391     Pte = HalAddressToPte(IOAPIC_BASE);
392     Pte->PageFrameNumber = IOAPIC_PHYS_BASE / PAGE_SIZE;
393     Pte->Valid = 1;
394     Pte->Write = 1;
395     Pte->Owner = 1;
396     Pte->CacheDisable = 1;
397     Pte->Global = 1;
398     _ReadWriteBarrier();
399 
400     /* Setup a redirection entry */
401     ReDirReg.Vector = 0xFF;
402     ReDirReg.DeliveryMode = APIC_MT_Fixed;
403     ReDirReg.DestinationMode = APIC_DM_Physical;
404     ReDirReg.DeliveryStatus = 0;
405     ReDirReg.Polarity = 0;
406     ReDirReg.RemoteIRR = 0;
407     ReDirReg.TriggerMode = APIC_TGM_Edge;
408     ReDirReg.Mask = 1;
409     ReDirReg.Reserved = 0;
410     ReDirReg.Destination = 0;
411 
412     /* Loop all table entries */
413     for (Index = 0; Index < 24; Index++)
414     {
415         /* Initialize entry */
416         IOApicWrite(IOAPIC_REDTBL + 2 * Index, ReDirReg.Long0);
417         IOApicWrite(IOAPIC_REDTBL + 2 * Index + 1, ReDirReg.Long1);
418     }
419 
420     /* Init the vactor to index table */
421     for (Vector = 0; Vector <= 255; Vector++)
422     {
423         HalpVectorToIndex[Vector] = 0xFF;
424     }
425 
426     // HACK: Allocate all IRQs, should rather do that on demand
427     for (Index = 0; Index <= 15; Index++)
428     {
429         /* Map the IRQs to IRQLs like with the PIC */
430         HalpAllocateSystemInterrupt(Index, 27 - Index);
431     }
432 
433     /* Enable the timer interrupt */
434     ReDirReg.Vector = APIC_CLOCK_VECTOR;
435     ReDirReg.DeliveryMode = APIC_MT_Fixed;
436     ReDirReg.DestinationMode = APIC_DM_Physical;
437     ReDirReg.TriggerMode = APIC_TGM_Edge;
438     ReDirReg.Mask = 0;
439     ReDirReg.Destination = ApicRead(APIC_ID);
440     IOApicWrite(IOAPIC_REDTBL + 2 * APIC_CLOCK_INDEX, ReDirReg.Long0);
441 }
442 
443 VOID
444 NTAPI
445 HalpInitializePICs(IN BOOLEAN EnableInterrupts)
446 {
447     ULONG_PTR EFlags;
448 
449     /* Save EFlags and disable interrupts */
450     EFlags = __readeflags();
451     _disable();
452 
453     /* Initialize and mask the PIC */
454     HalpInitializeLegacyPICs();
455 
456     /* Initialize the I/O APIC */
457     ApicInitializeIOApic();
458 
459     /* Manually reserve some vectors */
460     HalpVectorToIndex[APIC_CLOCK_VECTOR] = 8;
461     HalpVectorToIndex[APC_VECTOR] = 99;
462     HalpVectorToIndex[DISPATCH_VECTOR] = 99;
463 
464     /* Set interrupt handlers in the IDT */
465     KeRegisterInterruptHandler(APIC_CLOCK_VECTOR, HalpClockInterrupt);
466 #ifndef _M_AMD64
467     KeRegisterInterruptHandler(APC_VECTOR, HalpApcInterrupt);
468     KeRegisterInterruptHandler(DISPATCH_VECTOR, HalpDispatchInterrupt);
469 #endif
470 
471     /* Register the vectors for APC and dispatch interrupts */
472     HalpRegisterVector(IDT_INTERNAL, 0, APC_VECTOR, APC_LEVEL);
473     HalpRegisterVector(IDT_INTERNAL, 0, DISPATCH_VECTOR, DISPATCH_LEVEL);
474 
475     /* Restore interrupt state */
476     if (EnableInterrupts) EFlags |= EFLAGS_INTERRUPT_MASK;
477     __writeeflags(EFlags);
478 }
479 
480 
481 /* SOFTWARE INTERRUPT TRAPS ***************************************************/
482 
483 #ifndef _M_AMD64
484 VOID
485 DECLSPEC_NORETURN
486 FASTCALL
487 HalpApcInterruptHandler(IN PKTRAP_FRAME TrapFrame)
488 {
489     KPROCESSOR_MODE ProcessorMode;
490     KIRQL OldIrql;
491     ASSERT(ApicGetProcessorIrql() == APC_LEVEL);
492 
493    /* Enter trap */
494     KiEnterInterruptTrap(TrapFrame);
495 
496 #ifdef APIC_LAZY_IRQL
497     if (!HalBeginSystemInterrupt(APC_LEVEL, APC_VECTOR, &OldIrql))
498     {
499         /* "Spurious" interrupt, exit the interrupt */
500         KiEoiHelper(TrapFrame);
501     }
502 #else
503     /* Save the old IRQL */
504     OldIrql = ApicGetCurrentIrql();
505     ASSERT(OldIrql < APC_LEVEL);
506 #endif
507 
508     /* Raise to APC_LEVEL */
509     ApicRaiseIrql(APC_LEVEL);
510 
511     /* End the interrupt */
512     ApicSendEOI();
513 
514     /* Kernel or user APC? */
515     if (KiUserTrap(TrapFrame)) ProcessorMode = UserMode;
516     else if (TrapFrame->EFlags & EFLAGS_V86_MASK) ProcessorMode = UserMode;
517     else ProcessorMode = KernelMode;
518 
519     /* Enable interrupts and call the kernel's APC interrupt handler */
520     _enable();
521     KiDeliverApc(ProcessorMode, NULL, TrapFrame);
522 
523     /* Disable interrupts */
524     _disable();
525 
526     /* Restore the old IRQL */
527     ApicLowerIrql(OldIrql);
528 
529     /* Exit the interrupt */
530     KiEoiHelper(TrapFrame);
531 }
532 
533 VOID
534 DECLSPEC_NORETURN
535 FASTCALL
536 HalpDispatchInterruptHandler(IN PKTRAP_FRAME TrapFrame)
537 {
538     KIRQL OldIrql;
539     ASSERT(ApicGetProcessorIrql() == DISPATCH_LEVEL);
540 
541    /* Enter trap */
542     KiEnterInterruptTrap(TrapFrame);
543 
544 #ifdef APIC_LAZY_IRQL
545     if (!HalBeginSystemInterrupt(DISPATCH_LEVEL, DISPATCH_VECTOR, &OldIrql))
546     {
547         /* "Spurious" interrupt, exit the interrupt */
548         KiEoiHelper(TrapFrame);
549     }
550 #else
551     /* Get the current IRQL */
552     OldIrql = ApicGetCurrentIrql();
553     ASSERT(OldIrql < DISPATCH_LEVEL);
554 #endif
555 
556     /* Raise to DISPATCH_LEVEL */
557     ApicRaiseIrql(DISPATCH_LEVEL);
558 
559     /* End the interrupt */
560     ApicSendEOI();
561 
562     /* Enable interrupts and call the kernel's DPC interrupt handler */
563     _enable();
564     KiDispatchInterrupt();
565     _disable();
566 
567     /* Restore the old IRQL */
568     ApicLowerIrql(OldIrql);
569 
570     /* Exit the interrupt */
571     KiEoiHelper(TrapFrame);
572 }
573 #endif
574 
575 
576 /* SOFTWARE INTERRUPTS ********************************************************/
577 
578 
579 VOID
580 FASTCALL
581 HalRequestSoftwareInterrupt(IN KIRQL Irql)
582 {
583     /* Convert irql to vector and request an interrupt */
584     ApicRequestInterrupt(IrqlToSoftVector(Irql), APIC_TGM_Edge);
585 }
586 
587 VOID
588 FASTCALL
589 HalClearSoftwareInterrupt(
590     IN KIRQL Irql)
591 {
592     /* Nothing to do */
593 }
594 
595 
596 /* SYSTEM INTERRUPTS **********************************************************/
597 
598 BOOLEAN
599 NTAPI
600 HalEnableSystemInterrupt(
601     IN ULONG Vector,
602     IN KIRQL Irql,
603     IN KINTERRUPT_MODE InterruptMode)
604 {
605     IOAPIC_REDIRECTION_REGISTER ReDirReg;
606     PKPRCB Prcb = KeGetCurrentPrcb();
607     UCHAR Index;
608     ASSERT(Irql <= HIGH_LEVEL);
609     ASSERT((IrqlToTpr(Irql) & 0xF0) == (Vector & 0xF0));
610 
611     /* Get the irq for this vector */
612     Index = HalpVectorToIndex[Vector];
613 
614     /* Check if its valid */
615     if (Index == 0xff)
616     {
617         /* Interrupt is not in use */
618         return FALSE;
619     }
620 
621     /* Read the redirection entry */
622     ReDirReg = ApicReadIORedirectionEntry(Index);
623 
624     /* Check if the interrupt was unused */
625     if (ReDirReg.Vector != Vector)
626     {
627         ReDirReg.Vector = Vector;
628         ReDirReg.DeliveryMode = APIC_MT_LowestPriority;
629         ReDirReg.DestinationMode = APIC_DM_Logical;
630         ReDirReg.Destination = 0;
631     }
632 
633     /* Check if the destination is logical */
634     if (ReDirReg.DestinationMode == APIC_DM_Logical)
635     {
636         /* Set the bit for this cpu */
637         ReDirReg.Destination |= ApicLogicalId(Prcb->Number);
638     }
639 
640     /* Set the trigger mode */
641     ReDirReg.TriggerMode = 1 - InterruptMode;
642 
643     /* Now unmask it */
644     ReDirReg.Mask = FALSE;
645 
646     /* Write back the entry */
647     ApicWriteIORedirectionEntry(Index, ReDirReg);
648 
649     return TRUE;
650 }
651 
652 VOID
653 NTAPI
654 HalDisableSystemInterrupt(
655     IN ULONG Vector,
656     IN KIRQL Irql)
657 {
658     IOAPIC_REDIRECTION_REGISTER ReDirReg;
659     UCHAR Index;
660     ASSERT(Irql <= HIGH_LEVEL);
661     ASSERT(Vector < RTL_NUMBER_OF(HalpVectorToIndex));
662 
663     Index = HalpVectorToIndex[Vector];
664 
665     /* Read lower dword of redirection entry */
666     ReDirReg.Long0 = IOApicRead(IOAPIC_REDTBL + 2 * Index);
667 
668     /* Mask it */
669     ReDirReg.Mask = 1;
670 
671     /* Write back lower dword */
672     IOApicWrite(IOAPIC_REDTBL + 2 * Irql, ReDirReg.Long0);
673 }
674 
675 #ifndef _M_AMD64
676 BOOLEAN
677 NTAPI
678 HalBeginSystemInterrupt(
679     IN KIRQL Irql,
680     IN ULONG Vector,
681     OUT PKIRQL OldIrql)
682 {
683     KIRQL CurrentIrql;
684 
685     /* Get the current IRQL */
686     CurrentIrql = ApicGetCurrentIrql();
687 
688 #ifdef APIC_LAZY_IRQL
689     /* Check if this interrupt is allowed */
690     if (CurrentIrql >= Irql)
691     {
692         IOAPIC_REDIRECTION_REGISTER RedirReg;
693         UCHAR Index;
694 
695         /* It is not, set the real Irql in the TPR! */
696         ApicWrite(APIC_TPR, IrqlToTpr(CurrentIrql));
697 
698         /* Save the new hard IRQL in the IRR field */
699         KeGetPcr()->IRR = CurrentIrql;
700 
701         /* End this interrupt */
702         ApicSendEOI();
703 
704         /* Get the irq for this vector */
705         Index = HalpVectorToIndex[Vector];
706 
707         /* Check if its valid */
708         if (Index != 0xff)
709         {
710             /* Read the I/O redirection entry */
711             RedirReg = ApicReadIORedirectionEntry(Index);
712 
713             /* Re-request the interrupt to be handled later */
714             ApicRequestInterrupt(Vector, (UCHAR)RedirReg.TriggerMode);
715        }
716        else
717        {
718             /* Re-request the interrupt to be handled later */
719             ApicRequestInterrupt(Vector, APIC_TGM_Edge);
720        }
721 
722         /* Pretend it was a spurious interrupt */
723         return FALSE;
724     }
725 #endif
726     /* Save the current IRQL */
727     *OldIrql = CurrentIrql;
728 
729     /* Set the new IRQL */
730     ApicRaiseIrql(Irql);
731 
732     /* Turn on interrupts */
733     _enable();
734 
735     /* Success */
736     return TRUE;
737 }
738 
739 VOID
740 NTAPI
741 HalEndSystemInterrupt(
742     IN KIRQL OldIrql,
743     IN PKTRAP_FRAME TrapFrame)
744 {
745     /* Send an EOI */
746     ApicSendEOI();
747 
748     /* Restore the old IRQL */
749     ApicLowerIrql(OldIrql);
750 }
751 
752 
753 /* IRQL MANAGEMENT ************************************************************/
754 
755 KIRQL
756 NTAPI
757 KeGetCurrentIrql(VOID)
758 {
759     /* Read the current TPR and convert it to an IRQL */
760     return ApicGetCurrentIrql();
761 }
762 
763 VOID
764 FASTCALL
765 KfLowerIrql(
766     IN KIRQL OldIrql)
767 {
768 #if DBG
769     /* Validate correct lower */
770     if (OldIrql > ApicGetCurrentIrql())
771     {
772         /* Crash system */
773         KeBugCheck(IRQL_NOT_LESS_OR_EQUAL);
774     }
775 #endif
776     /* Set the new IRQL */
777     ApicLowerIrql(OldIrql);
778 }
779 
780 KIRQL
781 FASTCALL
782 KfRaiseIrql(
783     IN KIRQL NewIrql)
784 {
785     KIRQL OldIrql;
786 
787     /* Read the current IRQL */
788     OldIrql = ApicGetCurrentIrql();
789 #if DBG
790     /* Validate correct raise */
791     if (OldIrql > NewIrql)
792     {
793         /* Crash system */
794         KeBugCheck(IRQL_NOT_GREATER_OR_EQUAL);
795     }
796 #endif
797     /* Convert the new IRQL to a TPR value and write the register */
798     ApicRaiseIrql(NewIrql);
799 
800     /* Return old IRQL */
801     return OldIrql;
802 }
803 
804 KIRQL
805 NTAPI
806 KeRaiseIrqlToDpcLevel(VOID)
807 {
808     return KfRaiseIrql(DISPATCH_LEVEL);
809 }
810 
811 KIRQL
812 NTAPI
813 KeRaiseIrqlToSynchLevel(VOID)
814 {
815     return KfRaiseIrql(SYNCH_LEVEL);
816 }
817 
818 #endif /* !_M_AMD64 */
819 
820