📄 apic.c
字号:
/*
* ReactOS kernel
* Copyright (C) 2004, 2005 ReactOS Team
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/* $Id: apic.c 24965 2006-11-29 10:19:00Z janderwald $
*
* COPYRIGHT: See COPYING in the top level directory
* PROJECT: ReactOS kernel
* FILE: hal/halx86/apic.c
* PURPOSE:
* PROGRAMMER:
*/
/* INCLUDE ***********************************************************************/
#include <hal.h>
#include <halfuncs.h> /* Not in PCH because only used for MP HAL */
#include <rtlfuncs.h> /* Not in PCH because only used for MP HAL */
#define NDEBUG
#include <debug.h>
/* GLOBALS ***********************************************************************/
ULONG CPUCount; /* Total number of CPUs */
ULONG BootCPU; /* Bootstrap processor */
ULONG OnlineCPUs; /* Bitmask of online CPUs */
CPU_INFO CPUMap[MAX_CPU]; /* Map of all CPUs in the system */
#ifdef CONFIG_SMP
PULONG BIOSBase; /* Virtual address of BIOS data segment */
PULONG CommonBase; /* Virtual address of common area */
#endif
PULONG APICBase = (PULONG)APIC_DEFAULT_BASE; /* Virtual address of local APIC */
ULONG APICMode; /* APIC mode at startup */
/* For debugging */
ULONG lastregr[MAX_CPU];
ULONG lastvalr[MAX_CPU];
ULONG lastregw[MAX_CPU];
ULONG lastvalw[MAX_CPU];
#ifdef CONFIG_SMP
#include <pshpack1.h>
typedef struct _COMMON_AREA_INFO
{
ULONG Stack; /* Location of AP stack */
ULONG PageDirectory; /* Page directory for an AP */
ULONG NtProcessStartup; /* Kernel entry point for an AP */
ULONG PaeModeEnabled; /* PAE mode is enabled */
ULONG Debug[16]; /* For debugging */
} COMMON_AREA_INFO, *PCOMMON_AREA_INFO;
#include <poppack.h>
#endif
CHAR *APstart, *APend;
#define BIOS_AREA 0x0
#define COMMON_AREA 0x2000
#define HZ (100)
#define APIC_DIVISOR (16)
#define CMOS_READ(address) { \
WRITE_PORT_UCHAR((PUCHAR)0x70, address)); \
READ_PORT_UCHAR((PUCHAR)0x71)); \
}
#define CMOS_WRITE(address, value) { \
WRITE_PORT_UCHAR((PUCHAR)0x70, address); \
WRITE_PORT_UCHAR((PUCHAR)0x71, value); \
}
extern ULONG_PTR KernelBase;
/* FUNCTIONS *********************************************************************/
extern ULONG Read8254Timer(VOID);
extern VOID WaitFor8254Wraparound(VOID);
extern VOID MpsTimerInterrupt(VOID);
extern VOID MpsErrorInterrupt(VOID);
extern VOID MpsSpuriousInterrupt(VOID);
extern VOID MpsIpiInterrupt(VOID);
ULONG APICGetMaxLVT(VOID)
{
ULONG tmp, ver, maxlvt;
tmp = APICRead(APIC_VER);
ver = GET_APIC_VERSION(tmp);
/* 82489DXs do not report # of LVT entries. */
maxlvt = APIC_INTEGRATED(ver) ? GET_APIC_MAXLVT(tmp) : 2;
return maxlvt;
}
VOID APICClear(VOID)
{
ULONG tmp, maxlvt;
maxlvt = APICGetMaxLVT();
/*
* Careful: we have to set masks only first to deassert
* any level-triggered sources.
*/
if (maxlvt >= 3)
{
tmp = ERROR_VECTOR;
APICWrite(APIC_LVT3, tmp | APIC_LVT3_MASKED);
}
tmp = APICRead(APIC_LVTT);
APICWrite(APIC_LVTT, tmp | APIC_LVT_MASKED);
tmp = APICRead(APIC_LINT0);
APICWrite(APIC_LINT0, tmp | APIC_LVT_MASKED);
tmp = APICRead(APIC_LINT1);
APICWrite(APIC_LINT1, tmp | APIC_LVT_MASKED);
if (maxlvt >= 4)
{
tmp = APICRead(APIC_LVTPC);
APICWrite(APIC_LVTPC, tmp | APIC_LVT_MASKED);
}
#if 0
if (maxlvt >= 5)
{
tmp = APICRead(APIC_LVTTHMR);
APICWrite(APIC_LVTTHMR, tmp | APIC_LVT_MASKED);
}
#endif
/*
* Clean APIC state for other OSs:
*/
APICWrite(APIC_LVTT, APIC_LVT_MASKED);
APICWrite(APIC_LINT0, APIC_LVT_MASKED);
APICWrite(APIC_LINT1, APIC_LVT_MASKED);
if (maxlvt >= 3)
{
APICWrite(APIC_LVT3, APIC_LVT3_MASKED);
}
if (maxlvt >= 4)
{
APICWrite(APIC_LVTPC, APIC_LVT_MASKED);
}
#if 0
if (maxlvt >= 5)
{
APICWrite(APIC_LVTTHMR, APIC_LVT_MASKED);
}
#endif
}
/* Enable symetric I/O mode ie. connect the BSP's local APIC to INT and NMI lines */
VOID EnableApicMode(VOID)
{
/*
* Do not trust the local APIC being empty at bootup.
*/
APICClear();
WRITE_PORT_UCHAR((PUCHAR)0x22, 0x70);
WRITE_PORT_UCHAR((PUCHAR)0x23, 0x01);
}
/* Disable symetric I/O mode ie. go to PIC mode */
__inline VOID DisableSMPMode(VOID)
{
/*
* Put the board back into PIC mode (has an effect
* only on certain older boards). Note that APIC
* interrupts, including IPIs, won't work beyond
* this point! The only exception are INIT IPIs.
*/
WRITE_PORT_UCHAR((PUCHAR)0x22, 0x70);
WRITE_PORT_UCHAR((PUCHAR)0x23, 0x00);
}
VOID DumpESR(VOID)
{
ULONG tmp;
if (APICGetMaxLVT() > 3)
{
APICWrite(APIC_ESR, 0);
}
tmp = APICRead(APIC_ESR);
DbgPrint("ESR %08x\n", tmp);
}
VOID APICDisable(VOID)
{
ULONG tmp;
APICClear();
/*
* Disable APIC (implies clearing of registers for 82489DX!).
*/
tmp = APICRead(APIC_SIVR);
tmp &= ~APIC_SIVR_ENABLE;
APICWrite(APIC_SIVR, tmp);
}
__inline ULONG _APICRead(ULONG Offset)
{
PULONG p;
p = (PULONG)((ULONG)APICBase + Offset);
return *p;
}
#if 0
__inline VOID APICWrite(ULONG Offset,
ULONG Value)
{
PULONG p;
p = (PULONG)((ULONG)APICBase + Offset);
*p = Value;
}
#else
__inline VOID APICWrite(ULONG Offset,
ULONG Value)
{
PULONG p;
ULONG CPU = (_APICRead(APIC_ID) & APIC_ID_MASK) >> 24;
lastregw[CPU] = Offset;
lastvalw[CPU] = Value;
p = (PULONG)((ULONG)APICBase + Offset);
*p = Value;
}
#endif
#if 0
__inline ULONG APICRead(ULONG Offset)
{
PULONG p;
p = (PULONG)((ULONG)APICBase + Offset);
return *p;
}
#else
__inline ULONG APICRead(ULONG Offset)
{
PULONG p;
ULONG CPU = (_APICRead(APIC_ID) & APIC_ID_MASK) >> 24;
lastregr[CPU] = Offset;
lastvalr[CPU] = 0;
p = (PULONG)((ULONG)APICBase + Offset);
lastvalr[CPU] = *p;
return lastvalr[CPU];
}
#endif
__inline VOID APICSendEOI(VOID)
{
// Send the EOI
APICWrite(APIC_EOI, 0);
}
static VOID APICDumpBit(ULONG base)
{
ULONG v, i, j;
DbgPrint("0123456789abcdef0123456789abcdef\n");
for (i = 0; i < 8; i++)
{
v = APICRead(base + i*0x10);
for (j = 0; j < 32; j++)
{
if (v & (1<<j))
DbgPrint("1");
else
DbgPrint("0");
}
DbgPrint("\n");
}
}
VOID APICDump(VOID)
/*
* Dump the contents of the local APIC registers
*/
{
ULONG v, ver, maxlvt;
ULONG r1, r2, w1, w2;
ULONG CPU = ThisCPU();;
r1 = lastregr[CPU];
r2 = lastvalr[CPU];
w1 = lastregw[CPU];
w2 = lastvalw[CPU];
DbgPrint("\nPrinting local APIC contents on CPU(%d):\n", ThisCPU());
v = APICRead(APIC_ID);
DbgPrint("... ID : %08x (%01x) ", v, GET_APIC_ID(v));
v = APICRead(APIC_VER);
DbgPrint("... VERSION: %08x\n", v);
ver = GET_APIC_VERSION(v);
maxlvt = APICGetMaxLVT();
v = APICRead(APIC_TPR);
DbgPrint("... TPR : %08x (%02x)", v, v & ~0);
if (APIC_INTEGRATED(ver))
{
/* !82489DX */
v = APICRead(APIC_APR);
DbgPrint("... APR : %08x (%02x)\n", v, v & ~0);
v = APICRead(APIC_PPR);
DbgPrint("... PPR : %08x\n", v);
}
v = APICRead(APIC_EOI);
DbgPrint("... EOI : %08x ! ", v);
v = APICRead(APIC_LDR);
DbgPrint("... LDR : %08x\n", v);
v = APICRead(APIC_DFR);
DbgPrint("... DFR : %08x ! ", v);
v = APICRead(APIC_SIVR);
DbgPrint("... SIVR : %08x\n", v);
if (0)
{
DbgPrint("... ISR field:\n");
APICDumpBit(APIC_ISR);
DbgPrint("... TMR field:\n");
APICDumpBit(APIC_TMR);
DbgPrint("... IRR field:\n");
APICDumpBit(APIC_IRR);
}
if (APIC_INTEGRATED(ver))
{
/* !82489DX */
if (maxlvt > 3)
{
/* Due to the Pentium erratum 3AP. */
APICWrite(APIC_ESR, 0);
}
v = APICRead(APIC_ESR);
DbgPrint("... ESR : %08x\n", v);
}
v = APICRead(APIC_ICR0);
DbgPrint("... ICR0 : %08x ! ", v);
v = APICRead(APIC_ICR1);
DbgPrint("... ICR1 : %08x ! ", v);
v = APICRead(APIC_LVTT);
DbgPrint("... LVTT : %08x\n", v);
if (maxlvt > 3)
{
/* PC is LVT#4. */
v = APICRead(APIC_LVTPC);
DbgPrint("... LVTPC : %08x ! ", v);
}
v = APICRead(APIC_LINT0);
DbgPrint("... LINT0 : %08x ! ", v);
v = APICRead(APIC_LINT1);
DbgPrint("... LINT1 : %08x\n", v);
if (maxlvt > 2)
{
v = APICRead(APIC_LVT3);
DbgPrint("... LVT3 : %08x\n", v);
}
v = APICRead(APIC_ICRT);
DbgPrint("... ICRT : %08x ! ", v);
v = APICRead(APIC_CCRT);
DbgPrint("... CCCT : %08x ! ", v);
v = APICRead(APIC_TDCR);
DbgPrint("... TDCR : %08x\n", v);
DbgPrint("\n");
DbgPrint("Last register read (offset): 0x%08X\n", r1);
DbgPrint("Last register read (value): 0x%08X\n", r2);
DbgPrint("Last register written (offset): 0x%08X\n", w1);
DbgPrint("Last register written (value): 0x%08X\n", w2);
DbgPrint("\n");
}
BOOLEAN VerifyLocalAPIC(VOID)
{
SIZE_T reg0, reg1;
ULONG l, h;
/* The version register is read-only in a real APIC */
reg0 = APICRead(APIC_VER);
DPRINT1("Getting VERSION: %x\n", reg0);
APICWrite(APIC_VER, reg0 ^ APIC_VER_MASK);
reg1 = APICRead(APIC_VER);
DPRINT1("Getting VERSION: %x\n", reg1);
/*
* The two version reads above should print the same
* numbers. If the second one is different, then we
* poke at a non-APIC.
*/
if (reg1 != reg0)
{
return FALSE;
}
/*
* Check if the version looks reasonably.
*/
reg1 = GET_APIC_VERSION(reg0);
if (reg1 == 0x00 || reg1 == 0xff)
{
return FALSE;
}
reg1 = APICGetMaxLVT();
if (reg1 < 0x02 || reg1 == 0xff)
{
return FALSE;
}
/*
* The ID register is read/write in a real APIC.
*/
reg0 = APICRead(APIC_ID);
DPRINT1("Getting ID: %x\n", reg0);
APICWrite(APIC_ID, reg0 ^ APIC_ID_MASK);
reg1 = APICRead(APIC_ID);
DPRINT1("Getting ID: %x\n", reg1);
APICWrite(APIC_ID, reg0);
if (reg1 != (reg0 ^ APIC_ID_MASK))
{
return FALSE;
}
Ke386Rdmsr(0x1b /*MSR_IA32_APICBASE*/, l, h);
if (!(l & /*MSR_IA32_APICBASE_ENABLE*/(1<<11)))
{
DPRINT1("Local APIC disabled by BIOS -- reenabling.\n");
l &= ~/*MSR_IA32_APICBASE_BASE*/(1<<11);
l |= /*MSR_IA32_APICBASE_ENABLE | APIC_DEFAULT_PHYS_BASE*/(1<<11)|0xfee00000;
Ke386Wrmsr(0x1b/*MSR_IA32_APICBASE*/, l, h);
}
return TRUE;
}
#ifdef CONFIG_SMP
VOID APICSendIPI(ULONG Target, ULONG Mode)
{
ULONG tmp, i, flags;
/* save flags and disable interrupts */
Ke386SaveFlags(flags);
_disable();
/* Wait up to 100ms for the APIC to become ready */
for (i = 0; i < 10000; i++)
{
tmp = APICRead(APIC_ICR0);
/* Check Delivery Status */
if ((tmp & APIC_ICR0_DS) == 0)
break;
KeStallExecutionProcessor(10);
}
if (i == 10000)
{
DPRINT1("CPU(%d) Previous IPI was not delivered after 100ms.\n", ThisCPU());
}
/* Setup the APIC to deliver the IPI */
DPRINT("%08x %x\n", SET_APIC_DEST_FIELD(Target), Target);
APICWrite(APIC_ICR1, SET_APIC_DEST_FIELD(Target));
if (Target == APIC_TARGET_SELF)
{
Mode |= APIC_ICR0_DESTS_SELF;
}
else if (Target == APIC_TARGET_ALL)
{
Mode |= APIC_ICR0_DESTS_ALL;
}
else if (Target == APIC_TARGET_ALL_BUT_SELF)
{
Mode |= APIC_ICR0_DESTS_ALL_BUT_SELF;
}
else
{
Mode |= APIC_ICR0_DESTS_FIELD;
}
/* Now, fire off the IPI */
APICWrite(APIC_ICR0, Mode);
/* Wait up to 100ms for the APIC to become ready */
for (i = 0; i < 10000; i++)
{
tmp = APICRead(APIC_ICR0);
/* Check Delivery Status */
if ((tmp & APIC_ICR0_DS) == 0)
break;
KeStallExecutionProcessor(10);
}
if (i == 10000)
{
DPRINT1("CPU(%d) Current IPI was not delivered after 100ms.\n", ThisCPU());
}
Ke386RestoreFlags(flags);
}
#endif
VOID APICSetup(VOID)
{
ULONG CPU, tmp;
CPU = ThisCPU();
// APICDump();
DPRINT1("CPU%d:\n", CPU);
DPRINT1(" Physical APIC id: %d\n", GET_APIC_ID(APICRead(APIC_ID)));
DPRINT1(" Logical APIC id: %d\n", GET_APIC_LOGICAL_ID(APICRead(APIC_LDR)));
DPRINT1("%08x %08x %08x\n", APICRead(APIC_ID), APICRead(APIC_LDR), APICRead(APIC_DFR));
/*
* Intel recommends to set DFR, LDR and TPR before enabling
* an APIC. See e.g. "AP-388 82489DX User's Manual" (Intel
* document number 292116). So here it goes...
*/
/*
* Put the APIC into flat delivery mode.
* Must be "all ones" explicitly for 82489DX.
*/
APICWrite(APIC_DFR, 0xFFFFFFFF);
/*
* Set up the logical destination ID.
*/
tmp = APICRead(APIC_LDR);
tmp &= ~APIC_LDR_MASK;
/*
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -