Something went wrong. Try again.
Reactos
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134/* * 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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. *//* * COPYRIGHT: See COPYING in the top level directory * PROJECT: ReactOS kernel * FILE: hal/halx86/mp/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_SMPPULONG 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
extern 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);}
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; LARGE_INTEGER MsrValue;
/* 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; }
MsrValue.QuadPart = __readmsr(0x1B /*MSR_IA32_APICBASE*/);
if (!(MsrValue.LowPart & /*MSR_IA32_APICBASE_ENABLE*/(1<<11))) { DPRINT1("Local APIC disabled by BIOS -- reenabling.\n"); MsrValue.LowPart &= ~/*MSR_IA32_APICBASE_BASE*/(1<<11); MsrValue.LowPart |= /*MSR_IA32_APICBASE_ENABLE | APIC_DEFAULT_PHYS_BASE*/(1<<11)|0xfee00000; __writemsr(0x1B /*MSR_IA32_APICBASE*/, MsrValue.HighPart); }
return TRUE;}
#ifdef CONFIG_SMPVOID APICSendIPI(ULONG Target, ULONG Mode){ ULONG tmp, i, flags;
/* save flags and disable interrupts */ flags = __readeflags(); _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()); } __writeeflags(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; /* * FIXME: * This works only up to 8 CPU's */ tmp |= (1 << (KeGetCurrentProcessorNumber() + 24)); APICWrite(APIC_LDR, tmp);
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)); DPRINT1("%d\n", CPUMap[CPU].APICId);
/* Accept only higher interrupts */ APICWrite(APIC_TPR, 0xef);
/* Enable local APIC */ tmp = APICRead(APIC_SIVR); tmp &= ~0xff; tmp |= APIC_SIVR_ENABLE;
#if 0 tmp &= ~APIC_SIVR_FOCUS;#else tmp |= APIC_SIVR_FOCUS;#endif
/* Set spurious interrupt vector */ tmp |= SPURIOUS_VECTOR; APICWrite(APIC_SIVR, tmp);
/* * Set up LVT0, LVT1: * * set up through-local-APIC on the BP's LINT0. This is not * strictly necessery in pure symmetric-IO mode, but sometimes * we delegate interrupts to the 8259A. */ tmp = APICRead(APIC_LINT0) & APIC_LVT_MASKED; if (CPU == BootCPU && (APICMode == amPIC || !tmp)) { tmp = APIC_DM_EXTINT; DPRINT1("enabled ExtINT on CPU#%d\n", CPU); } else { tmp = APIC_DM_EXTINT | APIC_LVT_MASKED; DPRINT1("masked ExtINT on CPU#%d\n", CPU); } APICWrite(APIC_LINT0, tmp);
/* * Only the BSP should see the LINT1 NMI signal, obviously. */ if (CPU == BootCPU) { tmp = APIC_DM_NMI; } else { tmp = APIC_DM_NMI | APIC_LVT_MASKED; } if (!APIC_INTEGRATED(CPUMap[CPU].APICVersion)) { /* 82489DX */ tmp |= APIC_LVT_LEVEL_TRIGGER; } APICWrite(APIC_LINT1, tmp);
if (APIC_INTEGRATED(CPUMap[CPU].APICVersion)) { /* !82489DX */ if (APICGetMaxLVT() > 3) { /* Due to the Pentium erratum 3AP */ APICWrite(APIC_ESR, 0); }
tmp = APICRead(APIC_ESR); DPRINT("ESR value before enabling vector: 0x%X\n", tmp);
/* Enable sending errors */ tmp = ERROR_VECTOR; APICWrite(APIC_LVT3, tmp);
/* * Spec says clear errors after enabling vector */ if (APICGetMaxLVT() > 3) { APICWrite(APIC_ESR, 0); } tmp = APICRead(APIC_ESR); DPRINT("ESR value after enabling vector: 0x%X\n", tmp); }}#ifdef CONFIG_SMPVOID APICSyncArbIDs(VOID){ ULONG i, tmp;
/* 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) { DPRINT("CPU(%d) APIC busy for 100ms.\n", ThisCPU()); }
DPRINT("Synchronizing Arb IDs.\n"); APICWrite(APIC_ICR0, APIC_ICR0_DESTS_ALL | APIC_ICR0_LEVEL | APIC_DM_INIT);}#endif
VOID MpsErrorHandler(VOID){ ULONG tmp1, tmp2;
APICDump();
tmp1 = APICRead(APIC_ESR); APICWrite(APIC_ESR, 0); tmp2 = APICRead(APIC_ESR); DPRINT1("APIC error on CPU(%d) ESR(%x)(%x)\n", ThisCPU(), tmp1, tmp2);
/* * Acknowledge the interrupt */ APICSendEOI();
/* Here is what the APIC error bits mean: * 0: Send CS error * 1: Receive CS error * 2: Send accept error * 3: Receive accept error * 4: Reserved * 5: Send illegal vector * 6: Received illegal vector * 7: Illegal register address */ DPRINT1("APIC error on CPU(%d) ESR(%x)(%x)\n", ThisCPU(), tmp1, tmp2); for (;;);}
VOID MpsSpuriousHandler(VOID){ ULONG tmp;
DPRINT("Spurious interrupt on CPU(%d)\n", ThisCPU());
tmp = APICRead(APIC_ISR + ((SPURIOUS_VECTOR & ~0x1f) >> 1)); if (tmp & (1 << (SPURIOUS_VECTOR & 0x1f))) { APICSendEOI(); return; }#if 0 /* No need to send EOI here */ APICDump();#endif}
#ifdef CONFIG_SMPVOID MpsIpiHandler(VOID){ KIRQL oldIrql;
HalBeginSystemInterrupt(IPI_LEVEL, IPI_VECTOR, &oldIrql); _enable();#if 0 DbgPrint("(%s:%d) MpsIpiHandler on CPU%d, current irql is %d\n", __FILE__,__LINE__, KeGetCurrentProcessorNumber(), KeGetCurrentIrql());#endif
KiIpiServiceRoutine(NULL, NULL);
#if 0 DbgPrint("(%s:%d) MpsIpiHandler on CPU%d done\n", __FILE__,__LINE__, KeGetCurrentProcessorNumber());#endif
_disable(); HalEndSystemInterrupt(oldIrql, 0);}#endif
VOIDMpsIRQTrapFrameToTrapFrame(PKIRQ_TRAPFRAME IrqTrapFrame, PKTRAP_FRAME TrapFrame){#ifdef _M_AMD64 UNIMPLEMENTED;#else TrapFrame->SegGs = (USHORT)IrqTrapFrame->Gs; TrapFrame->SegFs = (USHORT)IrqTrapFrame->Fs; TrapFrame->SegEs = (USHORT)IrqTrapFrame->Es; TrapFrame->SegDs = (USHORT)IrqTrapFrame->Ds; TrapFrame->Eax = IrqTrapFrame->Eax; TrapFrame->Ecx = IrqTrapFrame->Ecx; TrapFrame->Edx = IrqTrapFrame->Edx; TrapFrame->Ebx = IrqTrapFrame->Ebx; TrapFrame->HardwareEsp = IrqTrapFrame->Esp; TrapFrame->Ebp = IrqTrapFrame->Ebp; TrapFrame->Esi = IrqTrapFrame->Esi; TrapFrame->Edi = IrqTrapFrame->Edi; TrapFrame->Eip = IrqTrapFrame->Eip; TrapFrame->SegCs = IrqTrapFrame->Cs; TrapFrame->EFlags = IrqTrapFrame->Eflags;#endif}
VOIDMpsTimerHandler(ULONG Vector, PKIRQ_TRAPFRAME Trapframe){ KIRQL oldIrql; KTRAP_FRAME KernelTrapFrame;#if 0 ULONG CPU; static ULONG Count[MAX_CPU] = {0,};#endif HalBeginSystemInterrupt(LOCAL_TIMER_VECTOR, PROFILE_LEVEL, &oldIrql); _enable();
#if 0 CPU = ThisCPU(); if ((Count[CPU] % 100) == 0) { DbgPrint("(%s:%d) MpsTimerHandler on CPU%d, irql = %d, epi = %x, KPCR = %x\n", __FILE__, __LINE__, CPU, oldIrql,Trapframe->Eip, KeGetPcr()); } Count[CPU]++;#endif
/* FIXME: SMP is totally broken */ MpsIRQTrapFrameToTrapFrame(Trapframe, &KernelTrapFrame); if (KeGetCurrentProcessorNumber() == 0) { //KeUpdateSystemTime(&KernelTrapFrame, oldIrql); } else { //KeUpdateRunTime(&KernelTrapFrame, oldIrql); }
_disable(); HalEndSystemInterrupt (oldIrql, 0);}
VOID APICSetupLVTT(ULONG ClockTicks){ ULONG tmp;
tmp = GET_APIC_VERSION(APICRead(APIC_VER)); if (!APIC_INTEGRATED(tmp)) { tmp = SET_APIC_TIMER_BASE(APIC_TIMER_BASE_DIV) | APIC_LVT_PERIODIC | LOCAL_TIMER_VECTOR; } else { /* Periodic timer */ tmp = APIC_LVT_PERIODIC | LOCAL_TIMER_VECTOR; } APICWrite(APIC_LVTT, tmp);
tmp = APICRead(APIC_TDCR); tmp &= ~(APIC_TDCR_1 | APIC_TIMER_BASE_DIV); tmp |= APIC_TDCR_16; APICWrite(APIC_TDCR, tmp); APICWrite(APIC_ICRT, ClockTicks / APIC_DIVISOR);}
VOIDAPICCalibrateTimer(ULONG CPU){ ULARGE_INTEGER t1, t2; LONG tt1, tt2; BOOLEAN TSCPresent;
DPRINT("Calibrating APIC timer for CPU %d\n", CPU);
APICSetupLVTT(1000000000);
TSCPresent = KeGetCurrentPrcb()->FeatureBits & KF_RDTSC ? TRUE : FALSE;
/* * The timer chip counts down to zero. Let's wait * for a wraparound to start exact measurement: * (the current tick might have been already half done) */ //WaitFor8254Wraparound();
/* * We wrapped around just now. Let's start */ if (TSCPresent) { t1.QuadPart = (LONGLONG)__rdtsc(); } tt1 = APICRead(APIC_CCRT);
//WaitFor8254Wraparound();
tt2 = APICRead(APIC_CCRT); if (TSCPresent) { t2.QuadPart = (LONGLONG)__rdtsc(); CPUMap[CPU].CoreSpeed = (HZ * (ULONG)(t2.QuadPart - t1.QuadPart)); DPRINT("CPU clock speed is %ld.%04ld MHz.\n", CPUMap[CPU].CoreSpeed/1000000, CPUMap[CPU].CoreSpeed%1000000); KeGetCurrentPrcb()->MHz = CPUMap[CPU].CoreSpeed/1000000; }
CPUMap[CPU].BusSpeed = (HZ * (long)(tt1 - tt2) * APIC_DIVISOR);
/* Setup timer for normal operation */// APICSetupLVTT((CPUMap[CPU].BusSpeed / 1000000) * 100); // 100ns APICSetupLVTT((CPUMap[CPU].BusSpeed / 1000000) * 10000); // 10ms// APICSetupLVTT((CPUMap[CPU].BusSpeed / 1000000) * 100000); // 100ms
DPRINT("Host bus clock speed is %ld.%04ld MHz.\n", CPUMap[CPU].BusSpeed/1000000, CPUMap[CPU].BusSpeed%1000000);}
VOIDSetInterruptGate(ULONG index, ULONG_PTR address){#ifdef _M_AMD64 KIDTENTRY64 *idt;
idt = &KeGetPcr()->IdtBase[index];
idt->OffsetLow = address & 0xffff; idt->Selector = KGDT_64_R0_CODE; idt->IstIndex = 0; idt->Reserved0 = 0; idt->Type = 0x0e; idt->Dpl = 0; idt->Present = 1; idt->OffsetMiddle = (address >> 16) & 0xffff; idt->OffsetHigh = address >> 32; idt->Reserved1 = 0; idt->Alignment = 0;#else KIDTENTRY *idt; KIDT_ACCESS Access;
/* Set the IDT Access Bits */ Access.Reserved = 0; Access.Present = 1; Access.Dpl = 0; /* Kernel-Mode */ Access.SystemSegmentFlag = 0; Access.SegmentType = I386_INTERRUPT_GATE;
idt = (KIDTENTRY*)((ULONG)KeGetPcr()->IDT + index * sizeof(KIDTENTRY)); idt->Offset = (USHORT)(address & 0xffff); idt->Selector = KGDT_R0_CODE; idt->Access = Access.Value; idt->ExtendedOffset = (USHORT)(address >> 16);#endif}
VOID HaliInitBSP(VOID){#ifdef CONFIG_SMP PUSHORT ps;#endif
static BOOLEAN BSPInitialized = FALSE;
/* Only initialize the BSP once */ if (BSPInitialized) { ASSERT(FALSE); return; }
BSPInitialized = TRUE;
/* Setup interrupt handlers */ SetInterruptGate(LOCAL_TIMER_VECTOR, (ULONG_PTR)MpsTimerInterrupt); SetInterruptGate(ERROR_VECTOR, (ULONG_PTR)MpsErrorInterrupt); SetInterruptGate(SPURIOUS_VECTOR, (ULONG_PTR)MpsSpuriousInterrupt);#ifdef CONFIG_SMP SetInterruptGate(IPI_VECTOR, (ULONG_PTR)MpsIpiInterrupt);#endif DPRINT("APIC is mapped at 0x%X\n", APICBase);
if (VerifyLocalAPIC()) { DPRINT("APIC found\n"); } else { DPRINT("No APIC found\n"); ASSERT(FALSE); }
if (APICMode == amPIC) { EnableApicMode(); }
APICSetup();
#ifdef CONFIG_SMP /* BIOS data segment */ BIOSBase = (PULONG)BIOS_AREA;
/* Area for communicating with the APs */ CommonBase = (PULONG)COMMON_AREA;
/* Copy bootstrap code to common area */ memcpy((PVOID)((ULONG_PTR)CommonBase + PAGE_SIZE), &APstart, (ULONG_PTR)&APend - (ULONG_PTR)&APstart + 1);
/* Set shutdown code */ CMOS_WRITE(0xF, 0xA);
/* Set warm reset vector */ ps = (PUSHORT)((ULONG_PTR)BIOSBase + 0x467); *ps = (COMMON_AREA + PAGE_SIZE) & 0xF;
ps = (PUSHORT)((ULONG_PTR)BIOSBase + 0x469); *ps = (COMMON_AREA + PAGE_SIZE) >> 4;#endif
/* Calibrate APIC timer */ APICCalibrateTimer(BootCPU);}
#ifdef CONFIG_SMPVOIDHaliStartApplicationProcessor(ULONG Cpu, ULONG Stack){ ULONG tmp, maxlvt; PCOMMON_AREA_INFO Common; ULONG StartupCount; ULONG i, j; ULONG DeliveryStatus = 0; ULONG AcceptStatus = 0;
if (Cpu >= MAX_CPU || Cpu >= CPUCount || OnlineCPUs & (1 << Cpu)) { ASSERT(FALSE); } DPRINT1("Attempting to boot CPU %d\n", Cpu);
/* Send INIT IPI */
APICSendIPI(Cpu, APIC_DM_INIT|APIC_ICR0_LEVEL_ASSERT);
KeStallExecutionProcessor(200);
/* Deassert INIT */
APICSendIPI(Cpu, APIC_DM_INIT|APIC_ICR0_LEVEL_DEASSERT);
if (APIC_INTEGRATED(CPUMap[Cpu].APICVersion)) { /* Clear APIC errors */ APICWrite(APIC_ESR, 0); tmp = (APICRead(APIC_ESR) & APIC_ESR_MASK); }
Common = (PCOMMON_AREA_INFO)CommonBase;
/* Write the location of the AP stack */ Common->Stack = (ULONG)Stack; /* Write the page directory page */ Common->PageDirectory = __readcr3(); /* Write the kernel entry point */ Common->NtProcessStartup = (ULONG_PTR)RtlImageNtHeader((PVOID)KernelBase)->OptionalHeader.AddressOfEntryPoint + KernelBase; /* Write the state of the mae mode */ Common->PaeModeEnabled = __readcr4() & CR4_PAE ? 1 : 0;
DPRINT1("%x %x %x %x\n", Common->Stack, Common->PageDirectory, Common->NtProcessStartup, Common->PaeModeEnabled);
DPRINT("Cpu %d got stack at 0x%X\n", Cpu, Common->Stack);#if 0 for (j = 0; j < 16; j++) { Common->Debug[j] = 0; }#endif
maxlvt = APICGetMaxLVT();
/* Is this a local APIC or an 82489DX? */ StartupCount = (APIC_INTEGRATED(CPUMap[Cpu].APICVersion)) ? 2 : 0;
for (i = 1; i <= StartupCount; i++) { /* It's a local APIC, so send STARTUP IPI */ DPRINT("Sending startup signal %d\n", i); /* Clear errors */ APICWrite(APIC_ESR, 0); APICRead(APIC_ESR);
APICSendIPI(Cpu, APIC_DM_STARTUP | ((COMMON_AREA + PAGE_SIZE) >> 12)|APIC_ICR0_LEVEL_DEASSERT);
/* Wait up to 10ms for IPI to be delivered */ j = 0; do { KeStallExecutionProcessor(10);
/* Check Delivery Status */ DeliveryStatus = APICRead(APIC_ICR0) & APIC_ICR0_DS;
j++; } while ((DeliveryStatus) && (j < 1000));
KeStallExecutionProcessor(200);
/* * Due to the Pentium erratum 3AP. */ if (maxlvt > 3) { APICRead(APIC_SIVR); APICWrite(APIC_ESR, 0); }
AcceptStatus = APICRead(APIC_ESR) & APIC_ESR_MASK;
if (DeliveryStatus || AcceptStatus) { break; } }
if (DeliveryStatus) { DPRINT("STARTUP IPI for CPU %d was never delivered.\n", Cpu); }
if (AcceptStatus) { DPRINT("STARTUP IPI for CPU %d was never accepted.\n", Cpu); }
if (!(DeliveryStatus || AcceptStatus)) {
/* Wait no more than 5 seconds for processor to boot */ DPRINT("Waiting for 5 seconds for CPU %d to boot\n", Cpu);
/* Wait no more than 5 seconds */ for (j = 0; j < 50000; j++) { if (CPUMap[Cpu].Flags & CPU_ENABLED) { break; } KeStallExecutionProcessor(100); } }
if (CPUMap[Cpu].Flags & CPU_ENABLED) { DbgPrint("CPU %d is now running\n", Cpu); } else { DbgPrint("Initialization of CPU %d failed\n", Cpu); }
#if 0 DPRINT("Debug bytes are:\n");
for (j = 0; j < 4; j++) { DPRINT("0x%08X 0x%08X 0x%08X 0x%08X.\n", Common->Debug[j*4+0], Common->Debug[j*4+1], Common->Debug[j*4+2], Common->Debug[j*4+3]); }
#endif}
#endif
VOIDFASTCALLDECLSPEC_NORETURNHalpApcInterruptHandler(IN PKTRAP_FRAME TrapFrame){ /* Set up a fake INT Stack */ TrapFrame->EFlags = __readeflags(); TrapFrame->SegCs = KGDT_R0_CODE; TrapFrame->Eip = TrapFrame->Eax;
/* Build the trap frame */ KiEnterInterruptTrap(TrapFrame);
/* unimplemented */ UNIMPLEMENTED;
/* Exit the interrupt */ KiEoiHelper(TrapFrame);
}
/* EOF */