Start implementing APIC support, which is needed for both SMP and x64. It will use the local APIC + I/O APIC for interrupt control, the RTC instead of the PIT for the timer interrupt (PIT doesn't always work with I/O APIC), the APIC timer for profiling and finally the TSC for the performance counter and KeStallExecutionProcessor.
The code is incomplete and doesn't work yet

svn path=/trunk/; revision=53611
This commit is contained in:
Timo Kreuzer 2011-09-06 21:01:49 +00:00
parent 2020d7c71f
commit b5198d7d39
8 changed files with 1422 additions and 0 deletions

View file

@ -0,0 +1,693 @@
/*
* PROJECT: ReactOS HAL
* LICENSE: GNU GPL - See COPYING in the top level directory
* FILE: hal/halx86/generic/apic.c
* PURPOSE: HAL APIC Management and Control Code
* PROGRAMMERS: Timo Kreuzer (timo.kreuzer@reactos.org)
* REFERENCES: http://www.joseflores.com/docs/ExploringIrql.html
* http://www.codeproject.com/KB/system/soviet_kernel_hack.aspx
* http://bbs.unixmap.net/thread-2022-1-1.html
*/
/* INCLUDES *******************************************************************/
#include <hal.h>
#define NDEBUG
#include <debug.h>
#include "apic.h"
/* GLOBALS ********************************************************************/
UCHAR HalpVectorToIndex[256];
#ifndef _M_AMD64
static const UCHAR
HalpIRQLtoTPR[32] =
{
0x00, /* 0 PASSIVE_LEVEL */
0x3d, /* 1 APC_LEVEL */
0x41, /* 2 DISPATCH_LEVEL */
0x41, /* 3 \ */
0x51, /* 4 \ */
0x61, /* 5 | */
0x71, /* 6 | */
0x81, /* 7 | */
0x91, /* 8 | */
0xa1, /* 9 | */
0xb1, /* 10 | */
0xb1, /* 11 | */
0xb1, /* 12 | */
0xb1, /* 13 | */
0xb1, /* 14 | */
0xb1, /* 15 DEVICE IRQL */
0xb1, /* 16 | */
0xb1, /* 17 | */
0xb1, /* 18 | */
0xb1, /* 19 | */
0xb1, /* 20 | */
0xb1, /* 21 | */
0xb1, /* 22 | */
0xb1, /* 23 | */
0xb1, /* 24 | */
0xb1, /* 25 / */
0xb1, /* 26 / */
0xc1, /* 27 PROFILE_LEVEL */
0xd1, /* 28 CLOCK2_LEVEL */
0xe1, /* 29 IPI_LEVEL */
0xef, /* 30 POWER_LEVEL */
0xff, /* 31 HIGH_LEVEL */
};
static const KIRQL
HalVectorToIRQL[16] =
{
0, /* 00 PASSIVE_LEVEL */
0xff, /* 10 */
0xff, /* 20 */
1, /* 3D APC_LEVEL */
2, /* 41 DISPATCH_LEVEL */
4, /* 50 \ */
5, /* 60 \ */
6, /* 70 | */
7, /* 80 DEVICE IRQL */
8, /* 90 | */
9, /* A0 / */
10, /* B0 / */
27, /* C1 PROFILE_LEVEL */
28, /* D1 CLOCK2_LEVEL */
29, /* E1 IPI_LEVEL / EF POWER_LEVEL */
31, /* FF HIGH_LEVEL */
};
#endif
/* PRIVATE FUNCTIONS **********************************************************/
ULONG
FORCEINLINE
IOApicRead(UCHAR Register)
{
/* Select the register, then do the read */
*(volatile ULONG *)(IOAPIC_BASE + IOAPIC_IOREGSEL) = Register;
return *(volatile ULONG *)(IOAPIC_BASE + IOAPIC_IOWIN);
}
VOID
FORCEINLINE
IOApicWrite(UCHAR Register, ULONG Value)
{
/* Select the register, then do the write */
*(volatile ULONG *)(IOAPIC_BASE + IOAPIC_IOREGSEL) = Register;
*(volatile ULONG *)(IOAPIC_BASE + IOAPIC_IOWIN) = Value;
}
KIRQL
FORCEINLINE
ApicGetProcessorIrql(VOID)
{
/* Read the TPR and convert it to an IRQL */
return TprToIrql(ApicRead(APIC_PPR));
}
KIRQL
FORCEINLINE
ApicGetCurrentIrql(VOID)
{
/* Read the TPR and convert it to an IRQL */
return TprToIrql(ApicRead(APIC_TPR));
}
VOID
FORCEINLINE
ApicSetCurrentIrql(KIRQL Irql)
{
/* Convert IRQL and write the TPR */
ApicWrite(APIC_TPR, IrqlToTpr(Irql));
}
UCHAR
FASTCALL
HalpIrqToVector(UCHAR Irq)
{
IOAPIC_REDIRECTION_REGISTER ReDirReg;
/* Read low dword of the redirection entry */
ReDirReg.Long0 = IOApicRead(IOAPIC_REDTBL + 2 * Irq);
/* Return the vector */
return (UCHAR)ReDirReg.Vector;
}
KIRQL
FASTCALL
HalpVectorToIrql(UCHAR Vector)
{
return TprToIrql(Vector >> 2);
}
UCHAR
FASTCALL
HalpVectorToIrq(UCHAR Vector)
{
return HalpVectorToIndex[Vector];
}
VOID
NTAPI
HalpInitializeLegacyPIC(VOID)
{
I8259_ICW1 Icw1;
I8259_ICW2 Icw2;
I8259_ICW3 Icw3;
I8259_ICW4 Icw4;
/* Initialize ICW1 for master, interval 8, edge-triggered mode with ICW4 */
Icw1.NeedIcw4 = TRUE;
Icw1.OperatingMode = Cascade;
Icw1.Interval = Interval8;
Icw1.InterruptMode = EdgeTriggered;
Icw1.Init = TRUE;
Icw1.InterruptVectorAddress = 0;
__outbyte(PIC1_CONTROL_PORT, Icw1.Bits);
/* ICW2 - interrupt vector offset */
Icw2.Bits = PRIMARY_VECTOR_BASE;
__outbyte(PIC1_DATA_PORT, Icw2.Bits);
/* Connect slave to IRQ 2 */
Icw3.Bits = 0;
Icw3.SlaveIrq2 = TRUE;
__outbyte(PIC1_DATA_PORT, Icw3.Bits);
/* Enable 8086 mode, non-automatic EOI, non-buffered mode, non special fully nested mode */
Icw4.SystemMode = New8086Mode;
Icw4.EoiMode = NormalEoi;
Icw4.BufferedMode = NonBuffered;
Icw4.SpecialFullyNestedMode = FALSE;
Icw4.Reserved = 0;
__outbyte(PIC1_DATA_PORT, Icw4.Bits);
/* Mask all interrupts */
__outbyte(PIC1_DATA_PORT, 0xFF);
/* Initialize ICW1 for slave, interval 8, edge-triggered mode with ICW4 */
Icw1.NeedIcw4 = TRUE;
Icw1.InterruptMode = EdgeTriggered;
Icw1.OperatingMode = Cascade;
Icw1.Interval = Interval8;
Icw1.Init = TRUE;
Icw1.InterruptVectorAddress = 0; /* This is only used in MCS80/85 mode */
__outbyte(PIC2_CONTROL_PORT, Icw1.Bits);
/* Set interrupt vector base */
Icw2.Bits = PRIMARY_VECTOR_BASE + 8;
__outbyte(PIC2_DATA_PORT, Icw2.Bits);
/* Slave ID */
Icw3.Bits = 0;
Icw3.SlaveId = 2;
__outbyte(PIC2_DATA_PORT, Icw3.Bits);
/* Enable 8086 mode, non-automatic EOI, non-buffered mode, non special fully nested mode */
Icw4.SystemMode = New8086Mode;
Icw4.EoiMode = NormalEoi;
Icw4.BufferedMode = NonBuffered;
Icw4.SpecialFullyNestedMode = FALSE;
Icw4.Reserved = 0;
__outbyte(PIC2_DATA_PORT, Icw4.Bits);
/* Mask all interrupts */
__outbyte(PIC2_DATA_PORT, 0xFF);
}
VOID
ApicInitializeLocalApic(ULONG Cpu)
{
APIC_BASE_ADRESS_REGISTER BaseRegister;
APIC_SPURIOUS_INERRUPT_REGISTER SpIntRegister;
LVT_REGISTER LvtEntry;
/* Enable the APIC if it wasn't yet */
BaseRegister.Long = __readmsr(MSR_APIC_BASE);
BaseRegister.Enable = 1;
BaseRegister.BootStrapCPUCore = (Cpu == 0);
__writemsr(MSR_APIC_BASE, BaseRegister.Long);
DPRINT1("ApicBase for Cpu %u PhysicalAddress = %p\n", Cpu, BaseRegister.BaseAddress);
DPRINT1("ApicVersion = 0x%lx\n", ApicRead(0x30));
/* Set spurious vector and SoftwareEnable to 1 */
SpIntRegister.Long = ApicRead(APIC_SIVR);
SpIntRegister.Vector = APIC_SPURIOUS_VECTOR;
SpIntRegister.SoftwareEnable = 1;
SpIntRegister.FocusCPUCoreChecking = 0;
ApicWrite(APIC_SIVR, SpIntRegister.Long);
/* Set the spurious ISR */
KeRegisterInterruptHandler(APIC_SPURIOUS_VECTOR, ApicSpuriousService);
/* Create a template LVT */
LvtEntry.Long = 0;
LvtEntry.Vector = 0xFF;
LvtEntry.MessageType = APIC_MT_Fixed;
LvtEntry.DeliveryStatus = 0;
LvtEntry.RemoteIRR = 0;
LvtEntry.TriggerMode = APIC_TGM_Edge;
LvtEntry.Mask = 1;
LvtEntry.TimerMode = 0;
/* Initalize and mask LVTs */
ApicWrite(APIC_TMRLVTR, LvtEntry.Long);
ApicWrite(APIC_THRMLVTR, LvtEntry.Long);
ApicWrite(APIC_PCLVTR, LvtEntry.Long);
ApicWrite(APIC_EXT0LVTR, LvtEntry.Long);
ApicWrite(APIC_EXT1LVTR, LvtEntry.Long);
ApicWrite(APIC_EXT2LVTR, LvtEntry.Long);
ApicWrite(APIC_EXT3LVTR, LvtEntry.Long);
/* LINT0 */
LvtEntry.Vector = APIC_SPURIOUS_VECTOR;
LvtEntry.MessageType = APIC_MT_ExtInt;
ApicWrite(APIC_LINT0, LvtEntry.Long);
/* Enable LINT1 (NMI) */
LvtEntry.Mask = 0;
LvtEntry.Vector = APIC_NMI_VECTOR;
LvtEntry.MessageType = APIC_MT_NMI;
LvtEntry.TriggerMode = APIC_TGM_Level;
ApicWrite(APIC_LINT1, LvtEntry.Long);
/* Enable error LVTR */
LvtEntry.Vector = APIC_ERROR_VECTOR;
LvtEntry.MessageType = APIC_MT_Fixed;
ApicWrite(APIC_ERRLVTR, LvtEntry.Long);
DPRINT1("Error code = 0x%lx\n", ApicRead(0x280));
}
VOID
NTAPI
ApicInitializeProcessor(
IN ULONG ProcessorNumber,
IN PLOADER_PARAMETER_BLOCK LoaderBlock)
{
DPRINT1("ApicInitializeProcessor(%ld)\n", ProcessorNumber);
/* Initialize the local APIC for this cpu */
ApicInitializeLocalApic(ProcessorNumber);
/* Initialize the timer */
ApicInitializeTimer(ProcessorNumber);
}
VOID
FORCEINLINE
ApicWriteIORedirectionEntry(
UCHAR Index,
IOAPIC_REDIRECTION_REGISTER ReDirReg)
{
IOApicWrite(IOAPIC_REDTBL + 2 * Index, ReDirReg.Long0);
IOApicWrite(IOAPIC_REDTBL + 2 * Index + 1, ReDirReg.Long1);
}
IOAPIC_REDIRECTION_REGISTER
FORCEINLINE
ApicReadIORedirectionEntry(
UCHAR Index)
{
IOAPIC_REDIRECTION_REGISTER ReDirReg;
ReDirReg.Long0 = IOApicRead(IOAPIC_REDTBL + 2 * Index);
ReDirReg.Long1 = IOApicRead(IOAPIC_REDTBL + 2 * Index + 1);
return ReDirReg;
}
UCHAR
NTAPI
HalpAllocateSystemInterrupt(
IN UCHAR Irq,
IN KIRQL Irql)
{
IOAPIC_REDIRECTION_REGISTER ReDirReg;
IN UCHAR Vector;
/* Start with low vector */
Vector = IrqlToTpr(Irql);
/* Find an empty vector */
while (HalpVectorToIndex[Vector] != 0xFF)
{
Vector++;
/* Check if we went over the edge */
if (TprToIrql(Vector) > Irql)
{
/* Nothing free, return failure */
return 0;
}
}
/* Save irq in the table */
HalpVectorToIndex[Vector] = Irq;
/* Setup a redirection entry */
ReDirReg.Vector = Vector;
ReDirReg.DeliveryMode = APIC_MT_LowestPriority;
ReDirReg.DestinationMode = APIC_DM_Logical;
ReDirReg.DeliveryStatus = 0;
ReDirReg.Polarity = 0;
ReDirReg.RemoteIRR = 0;
ReDirReg.TriggerMode = APIC_TGM_Edge;
ReDirReg.Mask = 1;
ReDirReg.Reserved = 0;
ReDirReg.Destination = 0;
/* Initialize entry */
IOApicWrite(IOAPIC_REDTBL + 2 * Irq, ReDirReg.Long0);
IOApicWrite(IOAPIC_REDTBL + 2 * Irq + 1, ReDirReg.Long1);
return Vector;
}
VOID
NTAPI
ApicInitializeIOApic(VOID)
{
PHARDWARE_PTE Pte;
IOAPIC_REDIRECTION_REGISTER ReDirReg;
UCHAR Index;
ULONG Vector;
/* Map the I/O Apic page */
Pte = HalAddressToPte(IOAPIC_BASE);
Pte->PageFrameNumber = IOAPIC_PHYS_BASE / PAGE_SIZE;
Pte->Valid = 1;
Pte->Write = 1;
Pte->Owner = 1;
Pte->CacheDisable = 1;
Pte->Global = 1;
_ReadWriteBarrier();
/* Setup a redirection entry */
ReDirReg.Vector = 0xFF;
ReDirReg.DeliveryMode = APIC_MT_Fixed;
ReDirReg.DestinationMode = APIC_DM_Physical;
ReDirReg.DeliveryStatus = 0;
ReDirReg.Polarity = 0;
ReDirReg.RemoteIRR = 0;
ReDirReg.TriggerMode = APIC_TGM_Edge;
ReDirReg.Mask = 1;
ReDirReg.Reserved = 0;
ReDirReg.Destination = 0;
/* Loop all table entries */
for (Index = 0; Index < 24; Index++)
{
/* Initialize entry */
IOApicWrite(IOAPIC_REDTBL + 2 * Index, ReDirReg.Long0);
IOApicWrite(IOAPIC_REDTBL + 2 * Index + 1, ReDirReg.Long1);
}
/* Init the vactor to index table */
for (Vector = 0; Vector <= 255; Vector++)
{
HalpVectorToIndex[Vector] = 0xFF;
}
// HACK: Allocate all IRQs, should rather do that on demand
for (Index = 0; Index <= 15; Index++)
{
/* Map the IRQs to IRQLs like with the PIC */
HalpAllocateSystemInterrupt(Index, 27 - Index);
}
/* Enable the timer interrupt */
ReDirReg.Vector = APIC_CLOCK_VECTOR;
ReDirReg.DestinationMode = APIC_DM_Logical;
ReDirReg.TriggerMode = APIC_TGM_Edge;
ReDirReg.Mask = 0;
IOApicWrite(IOAPIC_REDTBL + 2 * APIC_CLOCK_INDEX, ReDirReg.Long0);
}
VOID
NTAPI
HalpInitializePICs(IN BOOLEAN EnableInterrupts)
{
ULONG_PTR EFlags;
/* Save EFlags and disable interrupts */
EFlags = __readeflags();
_disable();
/* Initialize the local APIC for this cpu */
ApicInitializeLocalApic(0);
/* Initialize and mask the PIC */
HalpInitializeLegacyPIC();
/* Initialize the I/O APIC */
ApicInitializeIOApic();
ApicWrite(APIC_EOI, 0);
/* Register interrupt handlers */
KeRegisterInterruptHandler(APIC_CLOCK_VECTOR, HalpClockInterrupt);
KeRegisterInterruptHandler(APC_VECTOR, HalpApcInterrupt);
KeRegisterInterruptHandler(DPC_VECTOR, HalpDispatchInterrupt);
// HACK, since we messed with the value, should init the local apic in
// HalInitializeProcessor instead
ApicSetCurrentIrql(APC_LEVEL);
ASSERT(ApicGetProcessorIrql() <= APC_LEVEL);
__debugbreak();
HalpInitializeClock();
//HalpCalibrateStallExecution();
_enable();
for (;;);
/* Restore interrupt state */
if (EnableInterrupts) EFlags |= EFLAGS_INTERRUPT_MASK;
__writeeflags(EFlags);
}
VOID
DECLSPEC_NORETURN
FASTCALL
HalpApcInterruptHandler(IN PKTRAP_FRAME TrapFrame)
{
ASSERT(ApicGetCurrentIrql() < APC_LEVEL);
ASSERT(ApicGetProcessorIrql() == APC_LEVEL);
UNIMPLEMENTED;
ASSERT(FALSE);
}
VOID
DECLSPEC_NORETURN
FASTCALL
HalpDispatchInterruptHandler(IN PKTRAP_FRAME TrapFrame)
{
KIRQL OldIrql = ApicGetCurrentIrql();
__debugbreak();
ASSERT(OldIrql < DISPATCH_LEVEL);
ASSERT(ApicGetProcessorIrql() == DISPATCH_LEVEL);
ApicSetCurrentIrql(DISPATCH_LEVEL);
/* Enable interrupts and call the kernel's DPC interrupt handler */
_enable();
KiDispatchInterrupt();
_disable();
ApicSetCurrentIrql(OldIrql);
ApicWrite(APIC_EOI, 0);
/* Exit the interrupt */
KiEoiHelper(TrapFrame);
}
/* PUBLIC FUNCTIONS ***********************************************************/
VOID
FASTCALL
HalRequestSoftwareInterrupt(IN KIRQL Irql)
{
APIC_COMMAND_REGISTER CommandRegister;
/* Setup the command register */
CommandRegister.Long0 = 0;
CommandRegister.Vector = IrqlToTpr(Irql);
CommandRegister.MessageType = APIC_MT_Fixed;
CommandRegister.TriggerMode = APIC_TGM_Edge;
CommandRegister.DestinationShortHand = APIC_DSH_Self;
/* Write the low dword to send the interrupt */
ApicWrite(APIC_ICR0, CommandRegister.Long0);
}
VOID
FASTCALL
HalClearSoftwareInterrupt(
IN KIRQL Irql)
{
/* Nothing to do */
}
BOOLEAN
NTAPI
HalEnableSystemInterrupt(
IN UCHAR Vector,
IN KIRQL Irql,
IN KINTERRUPT_MODE InterruptMode)
{
IOAPIC_REDIRECTION_REGISTER ReDirReg;
UCHAR Index;
ASSERT(Irql <= HIGH_LEVEL);
ASSERT((IrqlToTpr(Irql) & 0xF0) == (Vector & 0xF0));
Index = HalpVectorToIndex[Vector];
/* Read lower dword of redirection entry */
ReDirReg.Long0 = IOApicRead(IOAPIC_REDTBL + 2 * Index);
ReDirReg.Vector = Vector;
ReDirReg.DeliveryMode = APIC_MT_LowestPriority;
ReDirReg.DestinationMode = APIC_DM_Logical;
ReDirReg.TriggerMode = 1 - InterruptMode;
ReDirReg.Mask = FALSE;
/* Write back lower dword */
IOApicWrite(IOAPIC_REDTBL + 2 * Irql, ReDirReg.Long0);
return TRUE;
}
VOID
NTAPI
HalDisableSystemInterrupt(
IN UCHAR Vector,
IN KIRQL Irql)
{
IOAPIC_REDIRECTION_REGISTER ReDirReg;
UCHAR Index;
ASSERT(Irql <= HIGH_LEVEL);
Index = HalpVectorToIndex[Vector];
/* Read lower dword of redirection entry */
ReDirReg.Long0 = IOApicRead(IOAPIC_REDTBL + 2 * Index);
/* Mask it */
ReDirReg.Mask = 1;
/* Write back lower dword */
IOApicWrite(IOAPIC_REDTBL + 2 * Irql, ReDirReg.Long0);
}
BOOLEAN
NTAPI
HalBeginSystemInterrupt(
IN KIRQL Irql,
IN UCHAR Vector,
OUT PKIRQL OldIrql)
{
/* Get the current IRQL */
*OldIrql = ApicGetCurrentIrql();
/* Set the new IRQL */
ApicSetCurrentIrql(Irql);
/* Turn on interrupts */
_enable();
/* Success */
return TRUE;
}
VOID
NTAPI
HalEndSystemInterrupt(
IN KIRQL OldIrql,
IN PKTRAP_FRAME TrapFrame)
{
/* Restore the old IRQL */
ApicSetCurrentIrql(OldIrql);
/* Write 0 to the EndOfInterruptRegister for level triggered ints */
ApicWrite(APIC_EOI, 0);
}
#ifndef _M_AMD64
KIRQL
NTAPI
KeGetCurrentIrql(VOID)
{
/* Read the current TPR and convert it to an IRQL */
return ApicGetCurrentIrql();
}
VOID
FASTCALL
KfLowerIrql(
IN KIRQL OldIrql)
{
#if DBG
/* Validate correct lower */
if (OldIrql > ApicGetCurrentIrql())
{
/* Crash system */
KeBugCheck(IRQL_NOT_LESS_OR_EQUAL);
}
#endif
/* Convert the new IRQL to a TPR value and write the register */
ApicSetCurrentIrql(OldIrql);
}
KIRQL
FASTCALL
KfRaiseIrql(
IN KIRQL NewIrql)
{
KIRQL OldIrql;
/* Read the current TPR and convert it to an IRQL */
OldIrql = ApicGetCurrentIrql();
#if DBG
/* Validate correct raise */
if (OldIrql > NewIrql)
{
/* Crash system */
KeBugCheck(IRQL_NOT_GREATER_OR_EQUAL);
}
#endif
/* Convert the new IRQL to a TPR value and write the register */
ApicSetCurrentIrql(NewIrql);
return OldIrql;
}
KIRQL
NTAPI
KeRaiseIrqlToDpcLevel(VOID)
{
return KfRaiseIrql(DISPATCH_LEVEL);
}
KIRQL
NTAPI
KeRaiseIrqlToSynchLevel(VOID)
{
return KfRaiseIrql(SYNCH_LEVEL);
}
#endif /* !_M_AMD64 */

View file

@ -0,0 +1,271 @@
#ifdef _M_AMD64
#define APIC_BASE 0xfffffffffee00000ULL;
#define ZERO_VECTOR 0x00 // IRQL 00
#define APC_VECTOR 0x3D // IRQL 01
#define APIC_SPURIOUS_VECTOR 0x3f
#define DPC_VECTOR 0x41 // IRQL 02
#define APIC_GENERIC_VECTOR 0xC1 // IRQL 27
#define APIC_CLOCK_VECTOR 0xD1 // IRQL 28
#define APIC_SYNCH_VECTOR 0xD1 // IRQL 28
#define APIC_IPI_VECTOR 0xE1 // IRQL 29
#define APIC_ERROR_VECTOR 0xE3
#define POWERFAIL_VECTOR 0xEF // IRQL 30
#define APIC_PROFILE_VECTOR 0xFD // IRQL 31
#define APIC_NMI_VECTOR 0xFF
#define IrqlToTpr(Irql) (Irql << 4)
#define TprToIrql(Tpr) (Tpr >> 4)
#else
#define APIC_BASE 0xFFFE0000
#define IOAPIC_BASE 0xFFFE1000 // checkme
#define IOAPIC_PHYS_BASE 0xFEC00000
#define ZERO_VECTOR 0x00 // IRQL 00
#define APIC_SPURIOUS_VECTOR 0x1f
#define APC_VECTOR 0x3D // IRQL 01
#define DPC_VECTOR 0x41 // IRQL 02
#define APIC_GENERIC_VECTOR 0xC1 // IRQL 27
#define APIC_CLOCK_VECTOR 0xD1 // IRQL 28
#define APIC_SYNCH_VECTOR 0xD1 // IRQL 28
#define APIC_IPI_VECTOR 0xE1 // IRQL 29
#define APIC_ERROR_VECTOR 0xE3
#define POWERFAIL_VECTOR 0xEF // IRQL 30
#define APIC_PROFILE_VECTOR 0xFD // IRQL 31
#define APIC_NMI_VECTOR 0xFF
#define IrqlToTpr(Irql) (HalpIRQLtoTPR[Irql])
#define TprToIrql(Tpr) (HalVectorToIRQL[Tpr >> 4])
#endif
#define MSR_APIC_BASE 0x0000001B
#define APIC_CLOCK_INDEX 8
/* APIC Register Address Map */
#define APIC_ID 0x0020 /* Local APIC ID Register (R/W) */
#define APIC_VER 0x0030 /* Local APIC Version Register (R) */
#define APIC_TPR 0x0080 /* Task Priority Register (R/W) */
#define APIC_APR 0x0090 /* Arbitration Priority Register (R) */
#define APIC_PPR 0x00A0 /* Processor Priority Register (R) */
#define APIC_EOI 0x00B0 /* EOI Register (W) */
#define APIC_RRR 0x00C0 /* Remote Read Register () */
#define APIC_LDR 0x00D0 /* Logical Destination Register (R/W) */
#define APIC_DFR 0x00E0 /* Destination Format Register (0-27 R, 28-31 R/W) */
#define APIC_SIVR 0x00F0 /* Spurious Interrupt Vector Register (0-3 R, 4-9 R/W) */
#define APIC_ISR 0x0100 /* Interrupt Service Register 0-255 (R) */
#define APIC_TMR 0x0180 /* Trigger Mode Register 0-255 (R) */
#define APIC_IRR 0x0200 /* Interrupt Request Register 0-255 (r) */
#define APIC_ESR 0x0280 /* Error Status Register (R) */
#define APIC_ICR0 0x0300 /* Interrupt Command Register 0-31 (R/W) */
#define APIC_ICR1 0x0310 /* Interrupt Command Register 32-63 (R/W) */
#define APIC_TMRLVTR 0x0320 /* Timer Local Vector Table (R/W) */
#define APIC_THRMLVTR 0x0330 /* Thermal Local Vector Table */
#define APIC_PCLVTR 0x0340 /* Performance Counter Local Vector Table (R/W) */
#define APIC_LINT0 0x0350 /* LINT0 Local Vector Table (R/W) */
#define APIC_LINT1 0x0360 /* LINT1 Local Vector Table (R/W) */
#define APIC_ERRLVTR 0x0370 /* Error Local Vector Table (R/W) */
#define APIC_TICR 0x0380 /* Initial Count Register for Timer (R/W) */
#define APIC_TCCR 0x0390 /* Current Count Register for Timer (R) */
#define APIC_TDCR 0x03E0 /* Timer Divide Configuration Register (R/W) */
#define APIC_EAFR 0x0400 /* extended APIC Feature register (R/W) */
#define APIC_EACR 0x0410 /* Extended APIC Control Register (R/W) */
#define APIC_SEOI 0x0420 /* Specific End Of Interrupt Register (W) */
#define APIC_EXT0LVTR 0x0500 /* Extended Interrupt 0 Local Vector Table */
#define APIC_EXT1LVTR 0x0510 /* Extended Interrupt 1 Local Vector Table */
#define APIC_EXT2LVTR 0x0520 /* Extended Interrupt 2 Local Vector Table */
#define APIC_EXT3LVTR 0x0530 /* Extended Interrupt 3 Local Vector Table */
enum
{
APIC_MT_Fixed = 0,
APIC_MT_LowestPriority = 1,
APIC_MT_SMI = 2,
APIC_MT_RemoteRead = 3,
APIC_MT_NMI = 4,
APIC_MT_INIT = 5,
APIC_MT_Startup = 6,
APIC_MT_ExtInt = 7,
};
enum
{
APIC_TGM_Edge,
APIC_TGM_Level
};
enum
{
APIC_DM_Physical,
APIC_DM_Logical
};
enum
{
APIC_DSH_Destination,
APIC_DSH_Self,
APIC_DSH_AllIncludingSelf,
APIC_DSH_AllExclusingSelf
};
enum
{
TIMER_DV_DivideBy2 = 0,
TIMER_DV_DivideBy4 = 1,
TIMER_DV_DivideBy8 = 2,
TIMER_DV_DivideBy16 = 3,
TIMER_DV_DivideBy32 = 8,
TIMER_DV_DivideBy64 = 9,
TIMER_DV_DivideBy128 = 10,
TIMER_DV_DivideBy1 = 11,
};
typedef union _APIC_BASE_ADRESS_REGISTER
{
ULONG64 Long;
struct
{
ULONG64 Reserved1:8;
ULONG64 BootStrapCPUCore:1;
ULONG64 Reserved2:2;
ULONG64 Enable:1;
ULONG64 BaseAddress:40;
ULONG64 ReservedMBZ:12;
};
} APIC_BASE_ADRESS_REGISTER;
typedef union _APIC_SPURIOUS_INERRUPT_REGISTER
{
ULONG Long;
struct
{
ULONG Vector:8;
ULONG SoftwareEnable:1;
ULONG FocusCPUCoreChecking:1;
ULONG ReservedMBZ:22;
};
} APIC_SPURIOUS_INERRUPT_REGISTER;
typedef union
{
ULONG Long;
struct
{
ULONG Version:8;
ULONG ReservedMBZ:8;
ULONG MaxLVT:8;
ULONG ReservedMBZ1:7;
ULONG ExtRegSpacePresent:1;
};
} APIC_VERSION_REGISTER;
typedef union
{
ULONG Long;
struct
{
ULONG Version:1;
ULONG SEOIEnable:1;
ULONG ExtApicIdEnable:1;
ULONG ReservedMBZ:29;
};
} APIC_EXTENDED_CONTROL_REGISTER;
typedef union _APIC_COMMAND_REGISTER
{
ULONGLONG LongLong;
struct
{
ULONG Long0;
ULONG Long1;
};
struct
{
ULONGLONG Vector:8;
ULONGLONG MessageType:3;
ULONGLONG DestinationMode:1;
ULONGLONG DeliveryStatus:1;
ULONGLONG ReservedMBZ:1;
ULONGLONG Level:1;
ULONGLONG TriggerMode:1;
ULONGLONG RemoteReadStatus:2;
ULONGLONG DestinationShortHand:2;
ULONGLONG Reserved2MBZ:36;
ULONGLONG Destination:8;
};
} APIC_COMMAND_REGISTER;
typedef union
{
ULONG Long;
struct
{
ULONG Vector:8;
ULONG MessageType:3;
ULONG ReservedMBZ:1;
ULONG DeliveryStatus:1;
ULONG Reserved1MBZ:1;
ULONG RemoteIRR:1;
ULONG TriggerMode:1;
ULONG Mask:1;
ULONG TimerMode:1;
ULONG Reserved2MBZ:13;
};
} LVT_REGISTER;
enum
{
IOAPIC_IOREGSEL = 0x00,
IOAPIC_IOWIN = 0x10
};
enum
{
IOAPIC_ID = 0x00,
IOAPIC_VER = 0x01,
IOAPIC_ARB = 0x02,
IOAPIC_REDTBL = 0x28
};
typedef union _IOAPIC_REDIRECTION_REGISTER
{
ULONGLONG LongLong;
struct
{
ULONG Long0;
ULONG Long1;
};
struct
{
ULONGLONG Vector:8;
ULONGLONG DeliveryMode:3;
ULONGLONG DestinationMode:1;
ULONGLONG DeliveryStatus:1;
ULONGLONG Polarity:1;
ULONGLONG RemoteIRR:1;
ULONGLONG TriggerMode:1;
ULONGLONG Mask:1;
ULONGLONG Reserved:39;
ULONGLONG Destination:8;
};
} IOAPIC_REDIRECTION_REGISTER;
ULONG
FORCEINLINE
ApicRead(ULONG Offset)
{
return *(volatile ULONG *)(APIC_BASE + Offset);
}
VOID
FORCEINLINE
ApicWrite(ULONG Offset, ULONG Value)
{
*(volatile ULONG *)(APIC_BASE + Offset) = Value;
}
VOID
NTAPI
ApicInitializeTimer(ULONG Cpu);
VOID ApicSpuriousService(VOID);

View file

@ -0,0 +1,84 @@
/*
* PROJECT: ReactOS HAL
* LICENSE: GPL - See COPYING in the top level directory
* FILE: hal/halx86/apic/apictimer.c
* PURPOSE: System Profiling
* PROGRAMMERS: Timo Kreuzer (timo.kreuzer@reactos.org)
*/
/* INCLUDES ******************************************************************/
#include <hal.h>
#define NDEBUG
#include <debug.h>
#include "apic.h"
extern LARGE_INTEGER HalpCpuClockFrequency;
/* TIMER FUNCTIONS ************************************************************/
VOID
NTAPI
ApicSetTimerInterval(ULONG MicroSeconds)
{
LVT_REGISTER LvtEntry;
ULONGLONG TimerInterval;
/* Calculate the Timer interval */
TimerInterval = HalpCpuClockFrequency.QuadPart * MicroSeconds / 1000000;
/* Set the count interval */
ApicWrite(APIC_TICR, (ULONG)TimerInterval);
/* Set to periodic */
LvtEntry.Long = 0;
LvtEntry.TimerMode = 1;
LvtEntry.Vector = APIC_PROFILE_VECTOR;
LvtEntry.Mask = 0;
ApicWrite(APIC_TMRLVTR, LvtEntry.Long);
}
VOID
NTAPI
ApicInitializeTimer(ULONG Cpu)
{
/* Initialize the TSC */
//HalpInitializeTsc();
/* Set clock multiplier to 1 */
ApicWrite(APIC_TDCR, TIMER_DV_DivideBy1);
ApicSetTimerInterval(1000);
// KeSetTimeIncrement
}
/* PUBLIC FUNCTIONS ***********************************************************/
VOID
NTAPI
HalStartProfileInterrupt(IN KPROFILE_SOURCE ProfileSource)
{
UNIMPLEMENTED;
return;
}
VOID
NTAPI
HalStopProfileInterrupt(IN KPROFILE_SOURCE ProfileSource)
{
UNIMPLEMENTED;
return;
}
ULONG_PTR
NTAPI
HalSetProfileInterval(IN ULONG_PTR Interval)
{
UNIMPLEMENTED;
return Interval;
}

View file

@ -0,0 +1,36 @@
/*
* FILE: hal/halx86/apic/apictrap.S
* COPYRIGHT: See COPYING in the top level directory
* PURPOSE: System Traps, Entrypoints and Exitpoints
* PROGRAMMER: Timo Kreuzer (timo.kreuzer@reactos.org)
* NOTE: See asmmacro.S for the shared entry/exit code.
*/
/* INCLUDES ******************************************************************/
#include <asm.inc>
#include <internal/i386/asmmacro.S>
#ifdef _M_AMD64
#include <ksamd64.inc>
PUBLIC ApicSpuriousService
#else
#include <ks386.inc>
PUBLIC _ApicSpuriousService
#endif
.code
TRAP_ENTRY HalpTrap0D, 0
TRAP_ENTRY HalpApcInterrupt, KI_SOFTWARE_TRAP
TRAP_ENTRY HalpDispatchInterrupt, KI_PUSH_FAKE_ERROR_CODE
TRAP_ENTRY HalpClockInterrupt, KI_PUSH_FAKE_ERROR_CODE
TRAP_ENTRY HalpProfileInterrupt, KI_PUSH_FAKE_ERROR_CODE
FUNC ApicSpuriousService
int 3
iret
ENDFUNC ApicSpuriousService
END

View file

@ -0,0 +1,144 @@
/*
* PROJECT: ReactOS HAL
* LICENSE: GNU GPL - See COPYING in the top level directory
* FILE: hal/halx86/generic/apic.c
* PURPOSE: HAL APIC Management and Control Code
* PROGRAMMERS: Timo Kreuzer (timo.kreuzer@reactos.org)
* REFERENCES:
*/
/* INCLUDES *******************************************************************/
#include <hal.h>
#define NDEBUG
#include <debug.h>
/* GLOBALS ********************************************************************/
const UCHAR HalpClockVector = 0xD1;
BOOLEAN HalpClockSetMSRate;
UCHAR HalpNextMSRate;
UCHAR HalpCurrentRate = 9;
ULONG HalpCurrentTimeIncrement;
static UCHAR RtcLargestClockRate = 10;
ULONG
FORCEINLINE
RtcClockRateToIncrement(UCHAR Rate)
{
ULONG Freqency = ((32768 << 1) >> Rate);
return (1000000 + (Freqency/2)) / Freqency;
}
VOID
RtcSetClockRate(UCHAR ClockRate)
{
ULONG_PTR EFlags;
UCHAR RegisterA;
/* Disable interrupts */
EFlags = __readeflags();
_disable();
// TODO: disable NMI
/* Read value of register A */
RegisterA = HalpReadCmos(RTC_REGISTER_A);
/* Change lower 4 bits to new rate */
RegisterA &= 0xF0;
RegisterA |= ClockRate;
/* Write the new value */
HalpWriteCmos(RTC_REGISTER_A, RegisterA);
/* Restore interrupts if they were previously enabled */
__writeeflags(EFlags);
}
VOID
NTAPI
INIT_FUNCTION
HalpInitializeClock(VOID)
{
UCHAR RegisterB;
// TODO: disable NMI
/* Enable the periodic interrupt in the CMOS */
RegisterB = HalpReadCmos(RTC_REGISTER_B);
HalpWriteCmos(RTC_REGISTER_B, RegisterB | RTC_REG_B_PI);
// RtcSetClockRate(HalpCurrentRate);
}
VOID
FASTCALL
HalpClockInterruptHandler(IN PKTRAP_FRAME TrapFrame)
{
ULONG LastIncrement;
KIRQL Irql;
/* Enter trap */
KiEnterInterruptTrap(TrapFrame);
__debugbreak();
/* Start the interrupt */
if (HalBeginSystemInterrupt(CLOCK2_LEVEL, PRIMARY_VECTOR_BASE, &Irql))
{
/* Read register C, so that the next interrupt can happen */
HalpReadCmos(RTC_REGISTER_C);;
/* Save increment */
LastIncrement = HalpCurrentTimeIncrement;
/* Check if someone changed the time rate */
if (HalpClockSetMSRate)
{
/* Update the global values */
HalpCurrentRate = HalpNextMSRate;
HalpCurrentTimeIncrement = RtcClockRateToIncrement(HalpCurrentRate);
/* Set new clock rate */
RtcSetClockRate(HalpCurrentRate);
/* We're done */
HalpClockSetMSRate = FALSE;
}
/* Update the system time -- the kernel will exit this trap */
KeUpdateSystemTime(TrapFrame, LastIncrement, Irql);
}
/* Spurious, just end the interrupt */
KiEoiHelper(TrapFrame);
}
VOID
FASTCALL
HalpProfileInterruptHandler(IN PKTRAP_FRAME TrapFrame)
{
__debugbreak();
}
ULONG
NTAPI
HalSetTimeIncrement(IN ULONG Increment)
{
UCHAR Rate;
/* Lookup largest value below given Increment */
for (Rate = 2; Rate < RtcLargestClockRate; Rate++)
{
/* Check if this is the largest rate possible */
if (RtcClockRateToIncrement(Rate + 1) > Increment) break;
}
/* Set the rate and tell HAL we want to change it */
HalpNextMSRate = Rate;
HalpClockSetMSRate = TRUE;
/* Return the real increment */
return RtcClockRateToIncrement(Rate);
}

View file

@ -0,0 +1,140 @@
/*
* PROJECT: ReactOS HAL
* LICENSE: GPL - See COPYING in the top level directory
* FILE: hal/halamd64/generic/tsc.c
* PURPOSE: HAL Routines for TSC handling
* PROGRAMMERS: Timo Kreuzer (timo.kreuzer@reactos.org)
*/
/* INCLUDES ******************************************************************/
#include <hal.h>
#define NDEBUG
#include <debug.h>
#include "tsc.h"
LARGE_INTEGER HalpCpuClockFrequency = {INITIAL_STALL_COUNT * 1000000};
UCHAR TscCalibrationPhase;
LARGE_INTEGER TscCalibrationArray[NUM_SAMPLES];
extern const UCHAR HalpClockVector;
/* PRIVATE FUNCTIONS *********************************************************/
VOID
NTAPI
HalpInitializeTsc()
{
ULONG_PTR Flags;
KIDTENTRY OldIdtEntry, *IdtPointer;
PKPCR Pcr = KeGetPcr();
/* Check if the CPU supports RDTSC */
if (!(KeGetCurrentPrcb()->FeatureBits & KF_RDTSC))
{
KeBugCheck(HAL_INITIALIZATION_FAILED);
}
/* Save flags and disable interrupts */
Flags = __readeflags();
_disable();
__debugbreak();
/* Initialze the PIT */
//HalpInitializePIT();
/* Save old IDT entry */
IdtPointer = KiGetIdtEntry(Pcr, HalpClockVector);
OldIdtEntry = *IdtPointer;
/* Set the calibration ISR */
KeRegisterInterruptHandler(HalpClockVector, TscCalibrationISR);
/* Reset TSC value to 0 */
__writemsr(MSR_RDTSC, 0);
/* Enable the timer interupt */
HalEnableSystemInterrupt(HalpClockVector, CLOCK_LEVEL, Latched);
/* Wait for completion */
_enable();
while (TscCalibrationPhase < NUM_SAMPLES) _ReadWriteBarrier();
_disable();
/* Disable the timer interupt */
HalDisableSystemInterrupt(HalpClockVector, CLOCK_LEVEL);
/* Restore old IDT entry */
*IdtPointer = OldIdtEntry;
// do linear regression
/* Restore flags */
__writeeflags(Flags);
}
VOID
NTAPI
HalpCalibrateStallExecution(VOID)
{
// Timer interrupt is now active
HalpInitializeTsc();
KeGetPcr()->StallScaleFactor = (ULONG)(HalpCpuClockFrequency.QuadPart / 1000000);
}
/* PUBLIC FUNCTIONS ***********************************************************/
LARGE_INTEGER
NTAPI
KeQueryPerformanceCounter(
OUT PLARGE_INTEGER PerformanceFrequency OPTIONAL)
{
LARGE_INTEGER Result;
/* Make sure it's calibrated */
ASSERT(HalpCpuClockFrequency.QuadPart != 0);
/* Does the caller want the frequency? */
if (PerformanceFrequency)
{
/* Return tsc frequency */
*PerformanceFrequency = HalpCpuClockFrequency;
}
/* Return the current value */
Result.QuadPart = __rdtsc();
return Result;
}
VOID
NTAPI
KeStallExecutionProcessor(ULONG MicroSeconds)
{
ULONG64 StartTime, EndTime;
/* Get the initial time */
StartTime = __rdtsc();
/* Calculate the ending time */
EndTime = StartTime + HalpCpuClockFrequency.QuadPart * MicroSeconds;
/* Loop until time is elapsed */
while (__rdtsc() < EndTime);
}
VOID
NTAPI
HalCalibratePerformanceCounter(
IN volatile PLONG Count,
IN ULONGLONG NewCount)
{
UNIMPLEMENTED;
ASSERT(FALSE);
}

View file

@ -0,0 +1,15 @@
#define NUM_SAMPLES 4
#define MSR_RDTSC 0x10
#ifndef __ASM__
void TscCalibrationISR(void);
extern LARGE_INTEGER HalpCpuClockFrequency;
VOID NTAPI HalpInitializeTsc();
#define KiGetIdtEntry(Pcr, Vector) &((Pcr)->IDT[Vector])
#endif

View file

@ -0,0 +1,39 @@
#include <asm.inc>
#include "tsc.h"
.code
EXTERN _TscCalibrationPhase:BYTE
EXTERN _TscCalibrationArray:QWORD
PUBLIC _TscCalibrationISR
_TscCalibrationISR:
push eax
push ecx
push edx
/* The first thing we do is read the current TSC value */
rdtsc
/* Read the current phase */
movzx ecx, byte ptr ds:[_TscCalibrationPhase]
/* Check if we're already done */
cmp cl, NUM_SAMPLES
jnb _CalibrationISR_Exit
/* Store the current value */
mov dword ptr _TscCalibrationArray[ecx * 2], eax
mov dword ptr _TscCalibrationArray[ecx * 2 + 4], edx
/* Advance phase */
inc byte ptr ds:[_TscCalibrationPhase]
_CalibrationISR_Exit:
pop edx
pop ecx
pop eax
iretd
END