[HAL]
[reactos.git] / reactos / hal / halx86 / apic / apic.c
1 /*
2 * PROJECT: ReactOS HAL
3 * LICENSE: GNU GPL - See COPYING in the top level directory
4 * FILE: hal/halx86/generic/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
20 /* GLOBALS ********************************************************************/
21
22 UCHAR HalpVectorToIndex[256];
23
24 #ifndef _M_AMD64
25 static const UCHAR
26 HalpIRQLtoTPR[32] =
27 {
28 0x00, /* 0 PASSIVE_LEVEL */
29 0x3d, /* 1 APC_LEVEL */
30 0x41, /* 2 DISPATCH_LEVEL */
31 0x41, /* 3 \ */
32 0x51, /* 4 \ */
33 0x61, /* 5 | */
34 0x71, /* 6 | */
35 0x81, /* 7 | */
36 0x91, /* 8 | */
37 0xa1, /* 9 | */
38 0xb1, /* 10 | */
39 0xb1, /* 11 | */
40 0xb1, /* 12 | */
41 0xb1, /* 13 | */
42 0xb1, /* 14 | */
43 0xb1, /* 15 DEVICE IRQL */
44 0xb1, /* 16 | */
45 0xb1, /* 17 | */
46 0xb1, /* 18 | */
47 0xb1, /* 19 | */
48 0xb1, /* 20 | */
49 0xb1, /* 21 | */
50 0xb1, /* 22 | */
51 0xb1, /* 23 | */
52 0xb1, /* 24 | */
53 0xb1, /* 25 / */
54 0xb1, /* 26 / */
55 0xc1, /* 27 PROFILE_LEVEL */
56 0xd1, /* 28 CLOCK2_LEVEL */
57 0xe1, /* 29 IPI_LEVEL */
58 0xef, /* 30 POWER_LEVEL */
59 0xff, /* 31 HIGH_LEVEL */
60 };
61
62 static const KIRQL
63 HalVectorToIRQL[16] =
64 {
65 0, /* 00 PASSIVE_LEVEL */
66 0xff, /* 10 */
67 0xff, /* 20 */
68 1, /* 3D APC_LEVEL */
69 2, /* 41 DISPATCH_LEVEL */
70 4, /* 50 \ */
71 5, /* 60 \ */
72 6, /* 70 | */
73 7, /* 80 DEVICE IRQL */
74 8, /* 90 | */
75 9, /* A0 / */
76 10, /* B0 / */
77 27, /* C1 PROFILE_LEVEL */
78 28, /* D1 CLOCK2_LEVEL */
79 29, /* E1 IPI_LEVEL / EF POWER_LEVEL */
80 31, /* FF HIGH_LEVEL */
81 };
82 #endif
83
84 /* PRIVATE FUNCTIONS **********************************************************/
85
86 ULONG
87 FORCEINLINE
88 IOApicRead(UCHAR Register)
89 {
90 /* Select the register, then do the read */
91 *(volatile ULONG *)(IOAPIC_BASE + IOAPIC_IOREGSEL) = Register;
92 return *(volatile ULONG *)(IOAPIC_BASE + IOAPIC_IOWIN);
93 }
94
95 VOID
96 FORCEINLINE
97 IOApicWrite(UCHAR Register, ULONG Value)
98 {
99 /* Select the register, then do the write */
100 *(volatile ULONG *)(IOAPIC_BASE + IOAPIC_IOREGSEL) = Register;
101 *(volatile ULONG *)(IOAPIC_BASE + IOAPIC_IOWIN) = Value;
102 }
103
104 KIRQL
105 FORCEINLINE
106 ApicGetProcessorIrql(VOID)
107 {
108 /* Read the TPR and convert it to an IRQL */
109 return TprToIrql(ApicRead(APIC_PPR));
110 }
111
112 KIRQL
113 FORCEINLINE
114 ApicGetCurrentIrql(VOID)
115 {
116 /* Read the TPR and convert it to an IRQL */
117 return TprToIrql(ApicRead(APIC_TPR));
118 }
119
120 VOID
121 FORCEINLINE
122 ApicSetCurrentIrql(KIRQL Irql)
123 {
124 /* Convert IRQL and write the TPR */
125 ApicWrite(APIC_TPR, IrqlToTpr(Irql));
126 }
127
128 UCHAR
129 FASTCALL
130 HalpIrqToVector(UCHAR Irq)
131 {
132 IOAPIC_REDIRECTION_REGISTER ReDirReg;
133
134 /* Read low dword of the redirection entry */
135 ReDirReg.Long0 = IOApicRead(IOAPIC_REDTBL + 2 * Irq);
136
137 /* Return the vector */
138 return (UCHAR)ReDirReg.Vector;
139 }
140
141 KIRQL
142 FASTCALL
143 HalpVectorToIrql(UCHAR Vector)
144 {
145 return TprToIrql(Vector >> 2);
146 }
147
148 UCHAR
149 FASTCALL
150 HalpVectorToIrq(UCHAR Vector)
151 {
152 return HalpVectorToIndex[Vector];
153 }
154
155 VOID
156 NTAPI
157 HalpInitializeLegacyPIC(VOID)
158 {
159 I8259_ICW1 Icw1;
160 I8259_ICW2 Icw2;
161 I8259_ICW3 Icw3;
162 I8259_ICW4 Icw4;
163
164 /* Initialize ICW1 for master, interval 8, edge-triggered mode with ICW4 */
165 Icw1.NeedIcw4 = TRUE;
166 Icw1.OperatingMode = Cascade;
167 Icw1.Interval = Interval8;
168 Icw1.InterruptMode = EdgeTriggered;
169 Icw1.Init = TRUE;
170 Icw1.InterruptVectorAddress = 0;
171 __outbyte(PIC1_CONTROL_PORT, Icw1.Bits);
172
173 /* ICW2 - interrupt vector offset */
174 Icw2.Bits = PRIMARY_VECTOR_BASE;
175 __outbyte(PIC1_DATA_PORT, Icw2.Bits);
176
177 /* Connect slave to IRQ 2 */
178 Icw3.Bits = 0;
179 Icw3.SlaveIrq2 = TRUE;
180 __outbyte(PIC1_DATA_PORT, Icw3.Bits);
181
182 /* Enable 8086 mode, non-automatic EOI, non-buffered mode, non special fully nested mode */
183 Icw4.SystemMode = New8086Mode;
184 Icw4.EoiMode = NormalEoi;
185 Icw4.BufferedMode = NonBuffered;
186 Icw4.SpecialFullyNestedMode = FALSE;
187 Icw4.Reserved = 0;
188 __outbyte(PIC1_DATA_PORT, Icw4.Bits);
189
190 /* Mask all interrupts */
191 __outbyte(PIC1_DATA_PORT, 0xFF);
192
193 /* Initialize ICW1 for slave, interval 8, edge-triggered mode with ICW4 */
194 Icw1.NeedIcw4 = TRUE;
195 Icw1.InterruptMode = EdgeTriggered;
196 Icw1.OperatingMode = Cascade;
197 Icw1.Interval = Interval8;
198 Icw1.Init = TRUE;
199 Icw1.InterruptVectorAddress = 0; /* This is only used in MCS80/85 mode */
200 __outbyte(PIC2_CONTROL_PORT, Icw1.Bits);
201
202 /* Set interrupt vector base */
203 Icw2.Bits = PRIMARY_VECTOR_BASE + 8;
204 __outbyte(PIC2_DATA_PORT, Icw2.Bits);
205
206 /* Slave ID */
207 Icw3.Bits = 0;
208 Icw3.SlaveId = 2;
209 __outbyte(PIC2_DATA_PORT, Icw3.Bits);
210
211 /* Enable 8086 mode, non-automatic EOI, non-buffered mode, non special fully nested mode */
212 Icw4.SystemMode = New8086Mode;
213 Icw4.EoiMode = NormalEoi;
214 Icw4.BufferedMode = NonBuffered;
215 Icw4.SpecialFullyNestedMode = FALSE;
216 Icw4.Reserved = 0;
217 __outbyte(PIC2_DATA_PORT, Icw4.Bits);
218
219 /* Mask all interrupts */
220 __outbyte(PIC2_DATA_PORT, 0xFF);
221 }
222
223 VOID
224 NTAPI
225 ApicInitializeLocalApic(ULONG Cpu)
226 {
227 APIC_BASE_ADRESS_REGISTER BaseRegister;
228 APIC_SPURIOUS_INERRUPT_REGISTER SpIntRegister;
229 LVT_REGISTER LvtEntry;
230
231 /* Enable the APIC if it wasn't yet */
232 BaseRegister.Long = __readmsr(MSR_APIC_BASE);
233 BaseRegister.Enable = 1;
234 BaseRegister.BootStrapCPUCore = (Cpu == 0);
235 __writemsr(MSR_APIC_BASE, BaseRegister.Long);
236
237 /* Set spurious vector and SoftwareEnable to 1 */
238 SpIntRegister.Long = ApicRead(APIC_SIVR);
239 SpIntRegister.Vector = APIC_SPURIOUS_VECTOR;
240 SpIntRegister.SoftwareEnable = 1;
241 SpIntRegister.FocusCPUCoreChecking = 0;
242 ApicWrite(APIC_SIVR, SpIntRegister.Long);
243
244 /* Set the spurious ISR */
245 KeRegisterInterruptHandler(APIC_SPURIOUS_VECTOR, ApicSpuriousService);
246
247 /* Create a template LVT */
248 LvtEntry.Long = 0;
249 LvtEntry.Vector = 0xFF;
250 LvtEntry.MessageType = APIC_MT_Fixed;
251 LvtEntry.DeliveryStatus = 0;
252 LvtEntry.RemoteIRR = 0;
253 LvtEntry.TriggerMode = APIC_TGM_Edge;
254 LvtEntry.Mask = 1;
255 LvtEntry.TimerMode = 0;
256
257 /* Initalize and mask LVTs */
258 ApicWrite(APIC_TMRLVTR, LvtEntry.Long);
259 ApicWrite(APIC_THRMLVTR, LvtEntry.Long);
260 ApicWrite(APIC_PCLVTR, LvtEntry.Long);
261 ApicWrite(APIC_EXT0LVTR, LvtEntry.Long);
262 ApicWrite(APIC_EXT1LVTR, LvtEntry.Long);
263 ApicWrite(APIC_EXT2LVTR, LvtEntry.Long);
264 ApicWrite(APIC_EXT3LVTR, LvtEntry.Long);
265
266 /* LINT0 */
267 LvtEntry.Vector = APIC_SPURIOUS_VECTOR;
268 LvtEntry.MessageType = APIC_MT_ExtInt;
269 ApicWrite(APIC_LINT0, LvtEntry.Long);
270
271 /* Enable LINT1 (NMI) */
272 LvtEntry.Mask = 0;
273 LvtEntry.Vector = APIC_NMI_VECTOR;
274 LvtEntry.MessageType = APIC_MT_NMI;
275 LvtEntry.TriggerMode = APIC_TGM_Level;
276 ApicWrite(APIC_LINT1, LvtEntry.Long);
277
278 /* Enable error LVTR */
279 LvtEntry.Vector = APIC_ERROR_VECTOR;
280 LvtEntry.MessageType = APIC_MT_Fixed;
281 ApicWrite(APIC_ERRLVTR, LvtEntry.Long);
282
283 /* Set the IRQL from the PCR */
284 ApicSetCurrentIrql(KeGetPcr()->Irql);
285 }
286
287
288 VOID
289 FORCEINLINE
290 ApicWriteIORedirectionEntry(
291 UCHAR Index,
292 IOAPIC_REDIRECTION_REGISTER ReDirReg)
293 {
294 IOApicWrite(IOAPIC_REDTBL + 2 * Index, ReDirReg.Long0);
295 IOApicWrite(IOAPIC_REDTBL + 2 * Index + 1, ReDirReg.Long1);
296 }
297
298 IOAPIC_REDIRECTION_REGISTER
299 FORCEINLINE
300 ApicReadIORedirectionEntry(
301 UCHAR Index)
302 {
303 IOAPIC_REDIRECTION_REGISTER ReDirReg;
304
305 ReDirReg.Long0 = IOApicRead(IOAPIC_REDTBL + 2 * Index);
306 ReDirReg.Long1 = IOApicRead(IOAPIC_REDTBL + 2 * Index + 1);
307
308 return ReDirReg;
309 }
310
311 UCHAR
312 NTAPI
313 HalpAllocateSystemInterrupt(
314 IN UCHAR Irq,
315 IN KIRQL Irql)
316 {
317 IOAPIC_REDIRECTION_REGISTER ReDirReg;
318 IN UCHAR Vector;
319
320 /* Start with low vector */
321 Vector = IrqlToTpr(Irql);
322
323 /* Find an empty vector */
324 while (HalpVectorToIndex[Vector] != 0xFF)
325 {
326 Vector++;
327
328 /* Check if we went over the edge */
329 if (TprToIrql(Vector) > Irql)
330 {
331 /* Nothing free, return failure */
332 return 0;
333 }
334 }
335
336 /* Save irq in the table */
337 HalpVectorToIndex[Vector] = Irq;
338
339 /* Setup a redirection entry */
340 ReDirReg.Vector = Vector;
341 ReDirReg.DeliveryMode = APIC_MT_LowestPriority;
342 ReDirReg.DestinationMode = APIC_DM_Logical;
343 ReDirReg.DeliveryStatus = 0;
344 ReDirReg.Polarity = 0;
345 ReDirReg.RemoteIRR = 0;
346 ReDirReg.TriggerMode = APIC_TGM_Edge;
347 ReDirReg.Mask = 1;
348 ReDirReg.Reserved = 0;
349 ReDirReg.Destination = 0;
350
351 /* Initialize entry */
352 IOApicWrite(IOAPIC_REDTBL + 2 * Irq, ReDirReg.Long0);
353 IOApicWrite(IOAPIC_REDTBL + 2 * Irq + 1, ReDirReg.Long1);
354
355 return Vector;
356 }
357
358 VOID
359 NTAPI
360 ApicInitializeIOApic(VOID)
361 {
362 PHARDWARE_PTE Pte;
363 IOAPIC_REDIRECTION_REGISTER ReDirReg;
364 UCHAR Index;
365 ULONG Vector;
366
367 /* Map the I/O Apic page */
368 Pte = HalAddressToPte(IOAPIC_BASE);
369 Pte->PageFrameNumber = IOAPIC_PHYS_BASE / PAGE_SIZE;
370 Pte->Valid = 1;
371 Pte->Write = 1;
372 Pte->Owner = 1;
373 Pte->CacheDisable = 1;
374 Pte->Global = 1;
375 _ReadWriteBarrier();
376
377 /* Setup a redirection entry */
378 ReDirReg.Vector = 0xFF;
379 ReDirReg.DeliveryMode = APIC_MT_Fixed;
380 ReDirReg.DestinationMode = APIC_DM_Physical;
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 /* Loop all table entries */
390 for (Index = 0; Index < 24; Index++)
391 {
392 /* Initialize entry */
393 IOApicWrite(IOAPIC_REDTBL + 2 * Index, ReDirReg.Long0);
394 IOApicWrite(IOAPIC_REDTBL + 2 * Index + 1, ReDirReg.Long1);
395 }
396
397 /* Init the vactor to index table */
398 for (Vector = 0; Vector <= 255; Vector++)
399 {
400 HalpVectorToIndex[Vector] = 0xFF;
401 }
402
403 // HACK: Allocate all IRQs, should rather do that on demand
404 for (Index = 0; Index <= 15; Index++)
405 {
406 /* Map the IRQs to IRQLs like with the PIC */
407 HalpAllocateSystemInterrupt(Index, 27 - Index);
408 }
409
410 /* Enable the timer interrupt */
411 ReDirReg.Vector = APIC_CLOCK_VECTOR;
412 ReDirReg.DestinationMode = APIC_DM_Logical;
413 ReDirReg.TriggerMode = APIC_TGM_Edge;
414 ReDirReg.Mask = 0;
415 IOApicWrite(IOAPIC_REDTBL + 2 * APIC_CLOCK_INDEX, ReDirReg.Long0);
416
417 }
418
419 VOID
420 NTAPI
421 HalpInitializePICs(IN BOOLEAN EnableInterrupts)
422 {
423 ULONG_PTR EFlags;
424
425 /* Save EFlags and disable interrupts */
426 EFlags = __readeflags();
427 _disable();
428
429 /* Initialize and mask the PIC */
430 HalpInitializeLegacyPIC();
431
432 /* Initialize the I/O APIC */
433 ApicInitializeIOApic();
434 ApicWrite(APIC_EOI, 0);
435
436 /* Register interrupt handlers */
437 KeRegisterInterruptHandler(APIC_CLOCK_VECTOR, HalpClockInterrupt);
438 #ifndef _M_AMD64
439 KeRegisterInterruptHandler(APC_VECTOR, HalpApcInterrupt);
440 KeRegisterInterruptHandler(DPC_VECTOR, HalpDispatchInterrupt);
441 #endif
442
443 /* Restore interrupt state */
444 if (EnableInterrupts) EFlags |= EFLAGS_INTERRUPT_MASK;
445 __writeeflags(EFlags);
446 }
447
448 VOID
449 DECLSPEC_NORETURN
450 FASTCALL
451 HalpApcInterruptHandler(IN PKTRAP_FRAME TrapFrame)
452 {
453 ASSERT(ApicGetCurrentIrql() < APC_LEVEL);
454 ASSERT(ApicGetProcessorIrql() == APC_LEVEL);
455
456 UNIMPLEMENTED;
457 ASSERT(FALSE);
458 }
459
460 #ifndef _M_AMD64
461 VOID
462 DECLSPEC_NORETURN
463 FASTCALL
464 HalpDispatchInterruptHandler(IN PKTRAP_FRAME TrapFrame)
465 {
466 KIRQL OldIrql = ApicGetCurrentIrql();
467 __debugbreak();
468 ASSERT(OldIrql < DISPATCH_LEVEL);
469 ASSERT(ApicGetProcessorIrql() == DISPATCH_LEVEL);
470
471 ApicSetCurrentIrql(DISPATCH_LEVEL);
472
473 /* Enable interrupts and call the kernel's DPC interrupt handler */
474 _enable();
475 KiDispatchInterrupt();
476 _disable();
477
478 ApicSetCurrentIrql(OldIrql);
479
480 ApicWrite(APIC_EOI, 0);
481
482 /* Exit the interrupt */
483 KiEoiHelper(TrapFrame);
484 }
485 #endif
486
487 /* PUBLIC FUNCTIONS ***********************************************************/
488
489 VOID
490 FASTCALL
491 HalRequestSoftwareInterrupt(IN KIRQL Irql)
492 {
493 APIC_COMMAND_REGISTER CommandRegister;
494
495 /* Setup the command register */
496 CommandRegister.Long0 = 0;
497 CommandRegister.Vector = IrqlToTpr(Irql);
498 CommandRegister.MessageType = APIC_MT_Fixed;
499 CommandRegister.TriggerMode = APIC_TGM_Edge;
500 CommandRegister.DestinationShortHand = APIC_DSH_Self;
501
502 /* Write the low dword to send the interrupt */
503 ApicWrite(APIC_ICR0, CommandRegister.Long0);
504 }
505
506 VOID
507 FASTCALL
508 HalClearSoftwareInterrupt(
509 IN KIRQL Irql)
510 {
511 /* Nothing to do */
512 }
513
514 BOOLEAN
515 NTAPI
516 HalEnableSystemInterrupt(
517 IN UCHAR Vector,
518 IN KIRQL Irql,
519 IN KINTERRUPT_MODE InterruptMode)
520 {
521 IOAPIC_REDIRECTION_REGISTER ReDirReg;
522 UCHAR Index;
523 ASSERT(Irql <= HIGH_LEVEL);
524 ASSERT((IrqlToTpr(Irql) & 0xF0) == (Vector & 0xF0));
525
526 Index = HalpVectorToIndex[Vector];
527
528 /* Read lower dword of redirection entry */
529 ReDirReg.Long0 = IOApicRead(IOAPIC_REDTBL + 2 * Index);
530
531 ReDirReg.Vector = Vector;
532 ReDirReg.DeliveryMode = APIC_MT_LowestPriority;
533 ReDirReg.DestinationMode = APIC_DM_Logical;
534 ReDirReg.TriggerMode = 1 - InterruptMode;
535 ReDirReg.Mask = FALSE;
536
537 /* Write back lower dword */
538 IOApicWrite(IOAPIC_REDTBL + 2 * Irql, ReDirReg.Long0);
539
540 return TRUE;
541 }
542
543 VOID
544 NTAPI
545 HalDisableSystemInterrupt(
546 IN UCHAR Vector,
547 IN KIRQL Irql)
548 {
549 IOAPIC_REDIRECTION_REGISTER ReDirReg;
550 UCHAR Index;
551 ASSERT(Irql <= HIGH_LEVEL);
552
553 Index = HalpVectorToIndex[Vector];
554
555 /* Read lower dword of redirection entry */
556 ReDirReg.Long0 = IOApicRead(IOAPIC_REDTBL + 2 * Index);
557
558 /* Mask it */
559 ReDirReg.Mask = 1;
560
561 /* Write back lower dword */
562 IOApicWrite(IOAPIC_REDTBL + 2 * Irql, ReDirReg.Long0);
563 }
564
565 #ifndef _M_AMD64
566 BOOLEAN
567 NTAPI
568 HalBeginSystemInterrupt(
569 IN KIRQL Irql,
570 IN UCHAR Vector,
571 OUT PKIRQL OldIrql)
572 {
573 /* Get the current IRQL */
574 *OldIrql = ApicGetCurrentIrql();
575
576 /* Set the new IRQL */
577 ApicSetCurrentIrql(Irql);
578
579 /* Turn on interrupts */
580 _enable();
581
582 /* Success */
583 return TRUE;
584 }
585
586 VOID
587 NTAPI
588 HalEndSystemInterrupt(
589 IN KIRQL OldIrql,
590 IN PKTRAP_FRAME TrapFrame)
591 {
592 /* Restore the old IRQL */
593 ApicSetCurrentIrql(OldIrql);
594
595 /* Write 0 to the EndOfInterruptRegister for level triggered ints */
596 ApicWrite(APIC_EOI, 0);
597 }
598
599
600 KIRQL
601 NTAPI
602 KeGetCurrentIrql(VOID)
603 {
604 /* Read the current TPR and convert it to an IRQL */
605 return ApicGetCurrentIrql();
606 }
607
608 VOID
609 FASTCALL
610 KfLowerIrql(
611 IN KIRQL OldIrql)
612 {
613 #if DBG
614 /* Validate correct lower */
615 if (OldIrql > ApicGetCurrentIrql())
616 {
617 /* Crash system */
618 KeBugCheck(IRQL_NOT_LESS_OR_EQUAL);
619 }
620 #endif
621 /* Convert the new IRQL to a TPR value and write the register */
622 ApicSetCurrentIrql(OldIrql);
623 }
624
625 KIRQL
626 FASTCALL
627 KfRaiseIrql(
628 IN KIRQL NewIrql)
629 {
630 KIRQL OldIrql;
631
632 /* Read the current IRQL */
633 OldIrql = ApicGetCurrentIrql();
634 #if DBG
635 /* Validate correct raise */
636 if (OldIrql > NewIrql)
637 {
638 /* Crash system */
639 KeBugCheck(IRQL_NOT_GREATER_OR_EQUAL);
640 }
641 #endif
642 /* Convert the new IRQL to a TPR value and write the register */
643 ApicSetCurrentIrql(NewIrql);
644
645 /* Return old IRQL */
646 return OldIrql;
647 }
648
649 KIRQL
650 NTAPI
651 KeRaiseIrqlToDpcLevel(VOID)
652 {
653 return KfRaiseIrql(DISPATCH_LEVEL);
654 }
655
656 KIRQL
657 NTAPI
658 KeRaiseIrqlToSynchLevel(VOID)
659 {
660 return KfRaiseIrql(SYNCH_LEVEL);
661 }
662
663 #endif /* !_M_AMD64 */
664