xref: /reactos/hal/halx86/apic/apic.c (revision 9cfd8dd9)
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:      https://web.archive.org/web/20190407074221/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  *                  https://www.codemachine.com/article_interruptdispatching.html
11  *                  https://www.osronline.com/article.cfm%5Earticle=211.htm
12  */
13 
14 /* INCLUDES *******************************************************************/
15 
16 #include <hal.h>
17 #include "apicp.h"
18 #define NDEBUG
19 #include <debug.h>
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     ASSERT(Register <= 0x3F);
98     WRITE_REGISTER_ULONG((PULONG)(IOAPIC_BASE + IOAPIC_IOREGSEL), Register);
99     return READ_REGISTER_ULONG((PULONG)(IOAPIC_BASE + IOAPIC_IOWIN));
100 }
101 
102 FORCEINLINE
103 VOID
104 IOApicWrite(UCHAR Register, ULONG Value)
105 {
106     /* Select the register, then do the write */
107     ASSERT(Register <= 0x3F);
108     WRITE_REGISTER_ULONG((PULONG)(IOAPIC_BASE + IOAPIC_IOREGSEL), Register);
109     WRITE_REGISTER_ULONG((PULONG)(IOAPIC_BASE + IOAPIC_IOWIN), Value);
110 }
111 
112 FORCEINLINE
113 VOID
114 ApicWriteIORedirectionEntry(
115     UCHAR Index,
116     IOAPIC_REDIRECTION_REGISTER ReDirReg)
117 {
118     ASSERT(Index < APIC_MAX_IRQ);
119     IOApicWrite(IOAPIC_REDTBL + 2 * Index, ReDirReg.Long0);
120     IOApicWrite(IOAPIC_REDTBL + 2 * Index + 1, ReDirReg.Long1);
121 }
122 
123 FORCEINLINE
124 IOAPIC_REDIRECTION_REGISTER
125 ApicReadIORedirectionEntry(
126     UCHAR Index)
127 {
128     IOAPIC_REDIRECTION_REGISTER ReDirReg;
129 
130     ASSERT(Index < APIC_MAX_IRQ);
131     ReDirReg.Long0 = IOApicRead(IOAPIC_REDTBL + 2 * Index);
132     ReDirReg.Long1 = IOApicRead(IOAPIC_REDTBL + 2 * Index + 1);
133 
134     return ReDirReg;
135 }
136 
137 FORCEINLINE
138 VOID
139 ApicRequestSelfInterrupt(IN UCHAR Vector, UCHAR TriggerMode)
140 {
141     ULONG Flags;
142     APIC_INTERRUPT_COMMAND_REGISTER Icr;
143     APIC_INTERRUPT_COMMAND_REGISTER IcrStatus;
144 
145     /*
146      * The IRR registers are spaced 16 bytes apart and hold 32 status bits each.
147      * Pre-compute the register and bit that match our vector.
148      */
149     ULONG VectorHigh = Vector / 32;
150     ULONG VectorLow = Vector % 32;
151     ULONG Irr = APIC_IRR + 0x10 * VectorHigh;
152     ULONG IrrBit = 1UL << VectorLow;
153 
154     /* Setup the command register */
155     Icr.Long0 = 0;
156     Icr.Vector = Vector;
157     Icr.MessageType = APIC_MT_Fixed;
158     Icr.TriggerMode = TriggerMode;
159     Icr.DestinationShortHand = APIC_DSH_Self;
160 
161     /* Disable interrupts so that we can change IRR without being interrupted */
162     Flags = __readeflags();
163     _disable();
164 
165     /* Wait for the APIC to be idle */
166     do
167     {
168         IcrStatus.Long0 = ApicRead(APIC_ICR0);
169     } while (IcrStatus.DeliveryStatus);
170 
171     /* Write the low dword to send the interrupt */
172     ApicWrite(APIC_ICR0, Icr.Long0);
173 
174     /* Wait until we see the interrupt request.
175      * It will stay in requested state until we re-enable interrupts.
176      */
177     while (!(ApicRead(Irr) & IrrBit))
178     {
179         NOTHING;
180     }
181 
182     /* Finally, restore the original interrupt state */
183     if (Flags & EFLAGS_INTERRUPT_MASK)
184     {
185         _enable();
186     }
187 }
188 
189 FORCEINLINE
190 VOID
191 ApicSendEOI(void)
192 {
193     ApicWrite(APIC_EOI, 0);
194 }
195 
196 FORCEINLINE
197 KIRQL
198 ApicGetProcessorIrql(VOID)
199 {
200     /* Read the TPR and convert it to an IRQL */
201     return TprToIrql(ApicRead(APIC_PPR));
202 }
203 
204 FORCEINLINE
205 KIRQL
206 ApicGetCurrentIrql(VOID)
207 {
208 #ifdef _M_AMD64
209     return (KIRQL)__readcr8();
210 #elif defined(APIC_LAZY_IRQL)
211     /* Return the field in the PCR */
212     return (KIRQL)__readfsbyte(FIELD_OFFSET(KPCR, Irql));
213 #else
214     /* Read the TPR and convert it to an IRQL */
215     return TprToIrql(ApicRead(APIC_TPR));
216 #endif
217 }
218 
219 FORCEINLINE
220 VOID
221 ApicSetIrql(KIRQL Irql)
222 {
223 #ifdef _M_AMD64
224     __writecr8(Irql);
225 #elif defined(APIC_LAZY_IRQL)
226     __writefsbyte(FIELD_OFFSET(KPCR, Irql), Irql);
227 #else
228     /* Convert IRQL and write the TPR */
229     ApicWrite(APIC_TPR, IrqlToTpr(Irql));
230 #endif
231 }
232 #define ApicRaiseIrql ApicSetIrql
233 
234 #ifdef APIC_LAZY_IRQL
235 FORCEINLINE
236 VOID
237 ApicLowerIrql(KIRQL Irql)
238 {
239     __writefsbyte(FIELD_OFFSET(KPCR, Irql), Irql);
240 
241     /* Is the new Irql lower than set in the TPR? */
242     if (Irql < KeGetPcr()->IRR)
243     {
244         /* Save the new hard IRQL in the IRR field */
245         KeGetPcr()->IRR = Irql;
246 
247         /* Need to lower it back */
248         ApicWrite(APIC_TPR, IrqlToTpr(Irql));
249     }
250 }
251 #else
252 #define ApicLowerIrql ApicSetIrql
253 #endif
254 
255 UCHAR
256 FASTCALL
257 HalpIrqToVector(UCHAR Irq)
258 {
259     IOAPIC_REDIRECTION_REGISTER ReDirReg;
260 
261     /* Read low dword of the redirection entry */
262     ReDirReg.Long0 = IOApicRead(IOAPIC_REDTBL + 2 * Irq);
263 
264     /* Return the vector */
265     return (UCHAR)ReDirReg.Vector;
266 }
267 
268 KIRQL
269 FASTCALL
270 HalpVectorToIrql(UCHAR Vector)
271 {
272     return TprToIrql(Vector);
273 }
274 
275 UCHAR
276 FASTCALL
277 HalpVectorToIrq(UCHAR Vector)
278 {
279     return HalpVectorToIndex[Vector];
280 }
281 
282 VOID
283 NTAPI
284 HalpSendEOI(VOID)
285 {
286     ApicSendEOI();
287 }
288 
289 VOID
290 NTAPI
291 ApicInitializeLocalApic(ULONG Cpu)
292 {
293     APIC_BASE_ADDRESS_REGISTER BaseRegister;
294     APIC_SPURIOUS_INERRUPT_REGISTER SpIntRegister;
295     LVT_REGISTER LvtEntry;
296 
297     /* Enable the APIC if it wasn't yet */
298     BaseRegister.LongLong = __readmsr(MSR_APIC_BASE);
299     BaseRegister.Enable = 1;
300     BaseRegister.BootStrapCPUCore = (Cpu == 0);
301     __writemsr(MSR_APIC_BASE, BaseRegister.LongLong);
302 
303     /* Set spurious vector and SoftwareEnable to 1 */
304     SpIntRegister.Long = ApicRead(APIC_SIVR);
305     SpIntRegister.Vector = APIC_SPURIOUS_VECTOR;
306     SpIntRegister.SoftwareEnable = 1;
307     SpIntRegister.FocusCPUCoreChecking = 0;
308     ApicWrite(APIC_SIVR, SpIntRegister.Long);
309 
310     /* Read the version and save it globally */
311     if (Cpu == 0) ApicVersion = ApicRead(APIC_VER);
312 
313     /* Set the mode to flat (max 8 CPUs supported!) */
314     ApicWrite(APIC_DFR, APIC_DF_Flat);
315 
316     /* Set logical apic ID */
317     ApicWrite(APIC_LDR, ApicLogicalId(Cpu) << 24);
318 
319     /* Set the spurious ISR */
320     KeRegisterInterruptHandler(APIC_SPURIOUS_VECTOR, ApicSpuriousService);
321 
322     /* Create a template LVT */
323     LvtEntry.Long = 0;
324     LvtEntry.Vector = APIC_FREE_VECTOR;
325     LvtEntry.MessageType = APIC_MT_Fixed;
326     LvtEntry.DeliveryStatus = 0;
327     LvtEntry.RemoteIRR = 0;
328     LvtEntry.TriggerMode = APIC_TGM_Edge;
329     LvtEntry.Mask = 1;
330     LvtEntry.TimerMode = 0;
331 
332     /* Initialize and mask LVTs */
333     ApicWrite(APIC_TMRLVTR, LvtEntry.Long);
334     ApicWrite(APIC_THRMLVTR, LvtEntry.Long);
335     ApicWrite(APIC_PCLVTR, LvtEntry.Long);
336     ApicWrite(APIC_EXT0LVTR, LvtEntry.Long);
337     ApicWrite(APIC_EXT1LVTR, LvtEntry.Long);
338     ApicWrite(APIC_EXT2LVTR, LvtEntry.Long);
339     ApicWrite(APIC_EXT3LVTR, LvtEntry.Long);
340 
341     /* LINT0 */
342     LvtEntry.Vector = APIC_SPURIOUS_VECTOR;
343     LvtEntry.MessageType = APIC_MT_ExtInt;
344     ApicWrite(APIC_LINT0, LvtEntry.Long);
345 
346     /* Enable LINT1 (NMI) */
347     LvtEntry.Mask = 0;
348     LvtEntry.Vector = APIC_NMI_VECTOR;
349     LvtEntry.MessageType = APIC_MT_NMI;
350     LvtEntry.TriggerMode = APIC_TGM_Level;
351     ApicWrite(APIC_LINT1, LvtEntry.Long);
352 
353     /* Enable error LVTR */
354     LvtEntry.Vector = APIC_ERROR_VECTOR;
355     LvtEntry.MessageType = APIC_MT_Fixed;
356     ApicWrite(APIC_ERRLVTR, LvtEntry.Long);
357 
358     /* Set the IRQL from the PCR */
359     ApicSetIrql(KeGetPcr()->Irql);
360 #ifdef APIC_LAZY_IRQL
361     /* Save the new hard IRQL in the IRR field */
362     KeGetPcr()->IRR = KeGetPcr()->Irql;
363 #endif
364 }
365 
366 UCHAR
367 NTAPI
368 HalpAllocateSystemInterrupt(
369     _In_ UCHAR Irq,
370     _In_ UCHAR Vector)
371 {
372     IOAPIC_REDIRECTION_REGISTER ReDirReg;
373 
374     ASSERT(Irq < APIC_MAX_IRQ);
375     ASSERT(HalpVectorToIndex[Vector] == APIC_FREE_VECTOR);
376 
377     /* Setup a redirection entry */
378     ReDirReg.Vector = Vector;
379     ReDirReg.DeliveryMode = APIC_MT_LowestPriority;
380     ReDirReg.DestinationMode = APIC_DM_Logical;
381     ReDirReg.DeliveryStatus = 0;
382     ReDirReg.Polarity = 0;
383     ReDirReg.RemoteIRR = 0;
384     ReDirReg.TriggerMode = APIC_TGM_Edge;
385     ReDirReg.Mask = 1;
386     ReDirReg.Reserved = 0;
387     ReDirReg.Destination = 0;
388 
389     /* Initialize entry */
390     ApicWriteIORedirectionEntry(Irq, ReDirReg);
391 
392     /* Save irq in the table */
393     HalpVectorToIndex[Vector] = Irq;
394 
395     return Vector;
396 }
397 
398 ULONG
399 NTAPI
400 HalpGetRootInterruptVector(
401     _In_ ULONG BusInterruptLevel,
402     _In_ ULONG BusInterruptVector,
403     _Out_ PKIRQL OutIrql,
404     _Out_ PKAFFINITY OutAffinity)
405 {
406     UCHAR Vector;
407     KIRQL Irql;
408 
409     /* Get the vector currently registered */
410     Vector = HalpIrqToVector(BusInterruptLevel);
411 
412     /* Check if it's used */
413     if (Vector != APIC_FREE_VECTOR)
414     {
415         /* Calculate IRQL */
416         NT_ASSERT(HalpVectorToIndex[Vector] == BusInterruptLevel);
417         *OutIrql = HalpVectorToIrql(Vector);
418     }
419     else
420     {
421         ULONG Offset;
422 
423         /* Outer loop to find alternative slots, when all IRQLs are in use */
424         for (Offset = 0; Offset < 15; Offset++)
425         {
426             /* Loop allowed IRQL range */
427             for (Irql = CLOCK_LEVEL - 1; Irql >= CMCI_LEVEL; Irql--)
428             {
429                 /* Calculate the vactor */
430                 Vector = IrqlToTpr(Irql) + Offset;
431 
432                 /* Check if the vector is free */
433                 if (HalpVectorToIrq(Vector) == APIC_FREE_VECTOR)
434                 {
435                     /* Found one, allocate the interrupt */
436                     Vector = HalpAllocateSystemInterrupt(BusInterruptLevel, Vector);
437                     *OutIrql = Irql;
438                     goto Exit;
439                 }
440             }
441         }
442 
443         DPRINT1("Failed to get an interrupt vector for IRQ %lu\n", BusInterruptLevel);
444         *OutAffinity = 0;
445         *OutIrql = 0;
446         return 0;
447     }
448 
449 Exit:
450 
451     *OutAffinity = HalpDefaultInterruptAffinity;
452     ASSERT(HalpDefaultInterruptAffinity);
453 
454     return Vector;
455 }
456 
457 VOID
458 NTAPI
459 ApicInitializeIOApic(VOID)
460 {
461     PHARDWARE_PTE Pte;
462     IOAPIC_REDIRECTION_REGISTER ReDirReg;
463     UCHAR Index;
464     ULONG Vector;
465 
466     /* Map the I/O Apic page */
467     Pte = HalAddressToPte(IOAPIC_BASE);
468     Pte->PageFrameNumber = IOAPIC_PHYS_BASE / PAGE_SIZE;
469     Pte->Valid = 1;
470     Pte->Write = 1;
471     Pte->Owner = 1;
472     Pte->CacheDisable = 1;
473     Pte->Global = 1;
474     _ReadWriteBarrier();
475 
476     /* Setup a redirection entry */
477     ReDirReg.Vector = APIC_FREE_VECTOR;
478     ReDirReg.DeliveryMode = APIC_MT_Fixed;
479     ReDirReg.DestinationMode = APIC_DM_Physical;
480     ReDirReg.DeliveryStatus = 0;
481     ReDirReg.Polarity = 0;
482     ReDirReg.RemoteIRR = 0;
483     ReDirReg.TriggerMode = APIC_TGM_Edge;
484     ReDirReg.Mask = 1;
485     ReDirReg.Reserved = 0;
486     ReDirReg.Destination = 0;
487 
488     /* Loop all table entries */
489     for (Index = 0; Index < APIC_MAX_IRQ; Index++)
490     {
491         /* Initialize entry */
492         ApicWriteIORedirectionEntry(Index, ReDirReg);
493     }
494 
495     /* Init the vactor to index table */
496     for (Vector = 0; Vector <= 255; Vector++)
497     {
498         HalpVectorToIndex[Vector] = APIC_FREE_VECTOR;
499     }
500 
501     /* Enable the timer interrupt (but keep it masked) */
502     ReDirReg.Vector = APIC_CLOCK_VECTOR;
503     ReDirReg.DeliveryMode = APIC_MT_Fixed;
504     ReDirReg.DestinationMode = APIC_DM_Physical;
505     ReDirReg.TriggerMode = APIC_TGM_Edge;
506     ReDirReg.Mask = 1;
507     ReDirReg.Destination = ApicRead(APIC_ID);
508     ApicWriteIORedirectionEntry(APIC_CLOCK_INDEX, ReDirReg);
509 }
510 
511 VOID
512 NTAPI
513 HalpInitializePICs(IN BOOLEAN EnableInterrupts)
514 {
515     ULONG_PTR EFlags;
516 
517     /* Save EFlags and disable interrupts */
518     EFlags = __readeflags();
519     _disable();
520 
521     /* Initialize and mask the PIC */
522     HalpInitializeLegacyPICs();
523 
524     /* Initialize the I/O APIC */
525     ApicInitializeIOApic();
526 
527     /* Manually reserve some vectors */
528     HalpVectorToIndex[APC_VECTOR] = APIC_RESERVED_VECTOR;
529     HalpVectorToIndex[DISPATCH_VECTOR] = APIC_RESERVED_VECTOR;
530     HalpVectorToIndex[APIC_CLOCK_VECTOR] = 8;
531     HalpVectorToIndex[APIC_SPURIOUS_VECTOR] = APIC_RESERVED_VECTOR;
532 
533     /* Set interrupt handlers in the IDT */
534     KeRegisterInterruptHandler(APIC_CLOCK_VECTOR, HalpClockInterrupt);
535 #ifndef _M_AMD64
536     KeRegisterInterruptHandler(APC_VECTOR, HalpApcInterrupt);
537     KeRegisterInterruptHandler(DISPATCH_VECTOR, HalpDispatchInterrupt);
538 #endif
539 
540     /* Register the vectors for APC and dispatch interrupts */
541     HalpRegisterVector(IDT_INTERNAL, 0, APC_VECTOR, APC_LEVEL);
542     HalpRegisterVector(IDT_INTERNAL, 0, DISPATCH_VECTOR, DISPATCH_LEVEL);
543 
544     /* Restore interrupt state */
545     if (EnableInterrupts) EFlags |= EFLAGS_INTERRUPT_MASK;
546     __writeeflags(EFlags);
547 }
548 
549 
550 /* SOFTWARE INTERRUPT TRAPS ***************************************************/
551 
552 #ifndef _M_AMD64
553 VOID
554 DECLSPEC_NORETURN
555 FASTCALL
556 HalpApcInterruptHandler(IN PKTRAP_FRAME TrapFrame)
557 {
558     KPROCESSOR_MODE ProcessorMode;
559     KIRQL OldIrql;
560     ASSERT(ApicGetProcessorIrql() == APC_LEVEL);
561 
562    /* Enter trap */
563     KiEnterInterruptTrap(TrapFrame);
564 
565 #ifdef APIC_LAZY_IRQL
566     if (!HalBeginSystemInterrupt(APC_LEVEL, APC_VECTOR, &OldIrql))
567     {
568         /* "Spurious" interrupt, exit the interrupt */
569         KiEoiHelper(TrapFrame);
570     }
571 #else
572     /* Save the old IRQL */
573     OldIrql = ApicGetCurrentIrql();
574     ASSERT(OldIrql < APC_LEVEL);
575 #endif
576 
577     /* Raise to APC_LEVEL */
578     ApicRaiseIrql(APC_LEVEL);
579 
580     /* End the interrupt */
581     ApicSendEOI();
582 
583     /* Kernel or user APC? */
584     if (KiUserTrap(TrapFrame)) ProcessorMode = UserMode;
585     else if (TrapFrame->EFlags & EFLAGS_V86_MASK) ProcessorMode = UserMode;
586     else ProcessorMode = KernelMode;
587 
588     /* Enable interrupts and call the kernel's APC interrupt handler */
589     _enable();
590     KiDeliverApc(ProcessorMode, NULL, TrapFrame);
591 
592     /* Disable interrupts */
593     _disable();
594 
595     /* Restore the old IRQL */
596     ApicLowerIrql(OldIrql);
597 
598     /* Exit the interrupt */
599     KiEoiHelper(TrapFrame);
600 }
601 
602 VOID
603 DECLSPEC_NORETURN
604 FASTCALL
605 HalpDispatchInterruptHandler(IN PKTRAP_FRAME TrapFrame)
606 {
607     KIRQL OldIrql;
608     ASSERT(ApicGetProcessorIrql() == DISPATCH_LEVEL);
609 
610    /* Enter trap */
611     KiEnterInterruptTrap(TrapFrame);
612 
613 #ifdef APIC_LAZY_IRQL
614     if (!HalBeginSystemInterrupt(DISPATCH_LEVEL, DISPATCH_VECTOR, &OldIrql))
615     {
616         /* "Spurious" interrupt, exit the interrupt */
617         KiEoiHelper(TrapFrame);
618     }
619 #else
620     /* Get the current IRQL */
621     OldIrql = ApicGetCurrentIrql();
622     ASSERT(OldIrql < DISPATCH_LEVEL);
623 #endif
624 
625     /* Raise to DISPATCH_LEVEL */
626     ApicRaiseIrql(DISPATCH_LEVEL);
627 
628     /* End the interrupt */
629     ApicSendEOI();
630 
631     /* Enable interrupts and call the kernel's DPC interrupt handler */
632     _enable();
633     KiDispatchInterrupt();
634     _disable();
635 
636     /* Restore the old IRQL */
637     ApicLowerIrql(OldIrql);
638 
639     /* Exit the interrupt */
640     KiEoiHelper(TrapFrame);
641 }
642 #endif
643 
644 
645 /* SOFTWARE INTERRUPTS ********************************************************/
646 
647 
648 VOID
649 FASTCALL
650 HalRequestSoftwareInterrupt(IN KIRQL Irql)
651 {
652     /* Convert irql to vector and request an interrupt */
653     ApicRequestSelfInterrupt(IrqlToSoftVector(Irql), APIC_TGM_Edge);
654 }
655 
656 VOID
657 FASTCALL
658 HalClearSoftwareInterrupt(
659     IN KIRQL Irql)
660 {
661     /* Nothing to do */
662 }
663 
664 
665 /* SYSTEM INTERRUPTS **********************************************************/
666 
667 BOOLEAN
668 NTAPI
669 HalEnableSystemInterrupt(
670     IN ULONG Vector,
671     IN KIRQL Irql,
672     IN KINTERRUPT_MODE InterruptMode)
673 {
674     IOAPIC_REDIRECTION_REGISTER ReDirReg;
675     PKPRCB Prcb = KeGetCurrentPrcb();
676     UCHAR Index;
677     ASSERT(Irql <= HIGH_LEVEL);
678     ASSERT((IrqlToTpr(Irql) & 0xF0) == (Vector & 0xF0));
679 
680     /* Get the irq for this vector */
681     Index = HalpVectorToIndex[Vector];
682 
683     /* Check if its valid */
684     if (Index == APIC_FREE_VECTOR)
685     {
686         /* Interrupt is not in use */
687         return FALSE;
688     }
689 
690     /* Read the redirection entry */
691     ReDirReg = ApicReadIORedirectionEntry(Index);
692 
693     /* Check if the interrupt was unused */
694     if (ReDirReg.Vector != Vector)
695     {
696         ReDirReg.Vector = Vector;
697         ReDirReg.DeliveryMode = APIC_MT_LowestPriority;
698         ReDirReg.DestinationMode = APIC_DM_Logical;
699         ReDirReg.Destination = 0;
700     }
701 
702     /* Check if the destination is logical */
703     if (ReDirReg.DestinationMode == APIC_DM_Logical)
704     {
705         /* Set the bit for this cpu */
706         ReDirReg.Destination |= ApicLogicalId(Prcb->Number);
707     }
708 
709     /* Set the trigger mode */
710     ReDirReg.TriggerMode = 1 - InterruptMode;
711 
712     /* Now unmask it */
713     ReDirReg.Mask = FALSE;
714 
715     /* Write back the entry */
716     ApicWriteIORedirectionEntry(Index, ReDirReg);
717 
718     return TRUE;
719 }
720 
721 VOID
722 NTAPI
723 HalDisableSystemInterrupt(
724     IN ULONG Vector,
725     IN KIRQL Irql)
726 {
727     IOAPIC_REDIRECTION_REGISTER ReDirReg;
728     UCHAR Index;
729     ASSERT(Irql <= HIGH_LEVEL);
730     ASSERT(Vector < RTL_NUMBER_OF(HalpVectorToIndex));
731 
732     Index = HalpVectorToIndex[Vector];
733 
734     /* Read lower dword of redirection entry */
735     ReDirReg.Long0 = IOApicRead(IOAPIC_REDTBL + 2 * Index);
736 
737     /* Mask it */
738     ReDirReg.Mask = 1;
739 
740     /* Write back lower dword */
741     IOApicWrite(IOAPIC_REDTBL + 2 * Index, ReDirReg.Long0);
742 }
743 
744 #ifndef _M_AMD64
745 BOOLEAN
746 NTAPI
747 HalBeginSystemInterrupt(
748     IN KIRQL Irql,
749     IN ULONG Vector,
750     OUT PKIRQL OldIrql)
751 {
752     KIRQL CurrentIrql;
753 
754     /* Get the current IRQL */
755     CurrentIrql = ApicGetCurrentIrql();
756 
757 #ifdef APIC_LAZY_IRQL
758     /* Check if this interrupt is allowed */
759     if (CurrentIrql >= Irql)
760     {
761         IOAPIC_REDIRECTION_REGISTER RedirReg;
762         UCHAR Index;
763 
764         /* It is not, set the real Irql in the TPR! */
765         ApicWrite(APIC_TPR, IrqlToTpr(CurrentIrql));
766 
767         /* Save the new hard IRQL in the IRR field */
768         KeGetPcr()->IRR = CurrentIrql;
769 
770         /* End this interrupt */
771         ApicSendEOI();
772 
773         /* Get the irq for this vector */
774         Index = HalpVectorToIndex[Vector];
775 
776         /* Check if it's valid */
777         if (Index < APIC_MAX_IRQ)
778         {
779             /* Read the I/O redirection entry */
780             RedirReg = ApicReadIORedirectionEntry(Index);
781 
782             /* Re-request the interrupt to be handled later */
783             ApicRequestSelfInterrupt(Vector, (UCHAR)RedirReg.TriggerMode);
784        }
785        else
786        {
787             /* This should be a reserved vector! */
788             ASSERT(Index == APIC_RESERVED_VECTOR);
789 
790             /* Re-request the interrupt to be handled later */
791             ApicRequestSelfInterrupt(Vector, APIC_TGM_Edge);
792        }
793 
794         /* Pretend it was a spurious interrupt */
795         return FALSE;
796     }
797 #endif
798     /* Save the current IRQL */
799     *OldIrql = CurrentIrql;
800 
801     /* Set the new IRQL */
802     ApicRaiseIrql(Irql);
803 
804     /* Turn on interrupts */
805     _enable();
806 
807     /* Success */
808     return TRUE;
809 }
810 
811 VOID
812 NTAPI
813 HalEndSystemInterrupt(
814     IN KIRQL OldIrql,
815     IN PKTRAP_FRAME TrapFrame)
816 {
817     /* Send an EOI */
818     ApicSendEOI();
819 
820     /* Restore the old IRQL */
821     ApicLowerIrql(OldIrql);
822 }
823 
824 
825 /* IRQL MANAGEMENT ************************************************************/
826 
827 KIRQL
828 NTAPI
829 KeGetCurrentIrql(VOID)
830 {
831     /* Read the current TPR and convert it to an IRQL */
832     return ApicGetCurrentIrql();
833 }
834 
835 VOID
836 FASTCALL
837 KfLowerIrql(
838     IN KIRQL OldIrql)
839 {
840 #if DBG
841     /* Validate correct lower */
842     if (OldIrql > ApicGetCurrentIrql())
843     {
844         /* Crash system */
845         KeBugCheck(IRQL_NOT_LESS_OR_EQUAL);
846     }
847 #endif
848     /* Set the new IRQL */
849     ApicLowerIrql(OldIrql);
850 }
851 
852 KIRQL
853 FASTCALL
854 KfRaiseIrql(
855     IN KIRQL NewIrql)
856 {
857     KIRQL OldIrql;
858 
859     /* Read the current IRQL */
860     OldIrql = ApicGetCurrentIrql();
861 #if DBG
862     /* Validate correct raise */
863     if (OldIrql > NewIrql)
864     {
865         /* Crash system */
866         KeBugCheck(IRQL_NOT_GREATER_OR_EQUAL);
867     }
868 #endif
869     /* Convert the new IRQL to a TPR value and write the register */
870     ApicRaiseIrql(NewIrql);
871 
872     /* Return old IRQL */
873     return OldIrql;
874 }
875 
876 KIRQL
877 NTAPI
878 KeRaiseIrqlToDpcLevel(VOID)
879 {
880     return KfRaiseIrql(DISPATCH_LEVEL);
881 }
882 
883 KIRQL
884 NTAPI
885 KeRaiseIrqlToSynchLevel(VOID)
886 {
887     return KfRaiseIrql(SYNCH_LEVEL);
888 }
889 
890 #endif /* !_M_AMD64 */
891 
892