⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 apic.c

📁 这是一个开放源代码的与WINNT/WIN2K/WIN2003兼容的操作系统
💻 C
📖 第 1 页 / 共 2 页
字号:
/*
 *  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 + -