mirror of
https://github.com/reactos/reactos.git
synced 2024-12-28 10:04:49 +00:00
[HAL]
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:
parent
2020d7c71f
commit
b5198d7d39
8 changed files with 1422 additions and 0 deletions
693
reactos/hal/halx86/apic/apic.c
Normal file
693
reactos/hal/halx86/apic/apic.c
Normal 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 */
|
||||
|
271
reactos/hal/halx86/apic/apic.h
Normal file
271
reactos/hal/halx86/apic/apic.h
Normal 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);
|
||||
|
84
reactos/hal/halx86/apic/apictimer.c
Normal file
84
reactos/hal/halx86/apic/apictimer.c
Normal 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;
|
||||
}
|
36
reactos/hal/halx86/apic/apictrap.S
Normal file
36
reactos/hal/halx86/apic/apictrap.S
Normal 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
|
144
reactos/hal/halx86/apic/rtctimer.c
Normal file
144
reactos/hal/halx86/apic/rtctimer.c
Normal 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);
|
||||
}
|
140
reactos/hal/halx86/apic/tsc.c
Normal file
140
reactos/hal/halx86/apic/tsc.c
Normal 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);
|
||||
}
|
||||
|
15
reactos/hal/halx86/apic/tsc.h
Normal file
15
reactos/hal/halx86/apic/tsc.h
Normal 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
|
39
reactos/hal/halx86/apic/tsccal.S
Normal file
39
reactos/hal/halx86/apic/tsccal.S
Normal 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
|
Loading…
Reference in a new issue