Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

575 lines
14 KiB

  1. /*++
  2. Copyright (c) 1999-2001 Microsoft Corporation. All Rights Reserved.
  3. Module Name:
  4. apic.c
  5. Abstract:
  6. This module contains global variables and functions used to program the local APIC.
  7. The local APIC resides on the processor die of newer Intel and AMD processors. It is
  8. used to control various interrupt sources both local and external to the processor.
  9. Author:
  10. Joseph Ballantyne
  11. Environment:
  12. Kernel Mode
  13. Revision History:
  14. --*/
  15. #include "common.h"
  16. #include "apic.h"
  17. #include "irq.h"
  18. #include "msr.h"
  19. #include "rtp.h"
  20. #include "rt.h"
  21. #include "x86.h"
  22. #include "rtexcept.h"
  23. #ifndef UNDER_NT
  24. #include <vmm.h>
  25. #endif
  26. #pragma LOCKED_DATA
  27. // These globals contain addresses pointing at various local APIC registers.
  28. // They must be in locked memory as they will be used to program the local APIC
  29. // while in interrupt service routines.
  30. CHAR *ApicBase=NULL;
  31. volatile ULONG *ApicPerfInterrupt=NULL;
  32. volatile ULONG *ApicTimerInterrupt=NULL;
  33. volatile ULONG *ApicNmiInterrupt=NULL;
  34. volatile ULONG *ApicIntrInterrupt=NULL;
  35. // WARNING!!! Do NOT change the ApicTimerVector or ApicErrorVector default
  36. // settings to a different define, without also updating HookWindowsInterrupts
  37. // in rt.c! Otherwise any local apic errors will jump to some unknown and
  38. // incorrect interrupt handler instead of our own - since we will not have
  39. // hooked the error vector IDT entry.
  40. // Also make sure you also update rtexcept.c appropriately as well if any
  41. // of the below variables are initialized with different defines.
  42. ULONG ApicTimerVector=MASKABLEIDTINDEX;
  43. ULONG ApicPerfVector=RTINTERRUPT;
  44. ULONG ApicErrorVector=APICERRORIDTINDEX;
  45. ULONG ApicSpuriousVector=APICSPURIOUSIDTINDEX;
  46. extern ID OriginalApicSpuriousVector;
  47. NTSTATUS
  48. HookInterrupt (
  49. ULONG index,
  50. ID *originalvector,
  51. VOID (* handler)(VOID)
  52. );
  53. #pragma PAGEABLE_DATA
  54. #pragma PAGEABLE_CODE
  55. #ifndef UNDER_NT
  56. #pragma warning ( disable : 4035 )
  57. #endif
  58. PCHAR
  59. MapPhysToLinear (
  60. VOID *physicaladdress,
  61. ULONG numbytes,
  62. ULONG flags
  63. )
  64. /*++
  65. Routine Description:
  66. This routine is a wrapper for OS functions that map physical memory to linear address
  67. space. On Win9x it wraps MapPhysToLinear, on WinNT it wraps MmMapIoSpace.
  68. Arguments:
  69. physicaladdress - Supplies the physical address to be mapped into linear address space.
  70. numbytes - Supplies the size of the block of physical memory to be mapped.
  71. flags - Supplies flags controlling the characteristics of the linear/virtual memory.
  72. Return Value:
  73. Since this routine is really just a wrapper for OS functions on
  74. Win9x and WinNT, the return value differs depending on the OS.
  75. On both platforms if the mapping is successful, the return value is the linear
  76. address of the mapped physical address.
  77. On Win9x if the mapping fails, the function returns (-1).
  78. On WinNT if the mapping fails, the function returns NULL.
  79. Platform independent callers MUST check for returns of EITHER (-1) or NULL
  80. when checking for failure.
  81. --*/
  82. {
  83. #ifdef UNDER_NT
  84. PHYSICAL_ADDRESS address;
  85. address.QuadPart=(ULONGLONG)physicaladdress;
  86. return MmMapIoSpace(address, numbytes, flags);
  87. #else
  88. __asm push flags
  89. __asm push numbytes
  90. __asm push physicaladdress
  91. VMMCall( _MapPhysToLinear );
  92. __asm add esp,12
  93. #endif
  94. }
  95. #ifndef UNDER_NT
  96. #pragma warning ( default : 4035 )
  97. #endif
  98. BOOL
  99. InitializeAPICBase (
  100. VOID
  101. )
  102. /*++
  103. Routine Description:
  104. If this routine has already run successfully, then it simply returns TRUE.
  105. If not, then it first reads the physical address that the local APIC
  106. is mapped to. Then it maps that physical address to virtual memory
  107. and saves the virtual memory location in the global ApicBase. If the
  108. mapping fails, then it returns FALSE, otherwise it loads globals that
  109. point to specific local APIC interrupt control registers and returns
  110. TRUE.
  111. WARNING: Currently this routine ASSUMES processor support of the
  112. Intel local APIC mapping MSR.
  113. Arguments:
  114. None.
  115. Return Value:
  116. TRUE if local APIC already mapped, or if it is mapped successfully during the call.
  117. FALSE if mapping the local APIC fails.
  118. --*/
  119. {
  120. // If local APIC already mapped, simply return TRUE.
  121. if (ApicBase!=NULL && ApicBase!=(CHAR *)(-1)) {
  122. return TRUE;
  123. }
  124. // Read the local APIC physical location.
  125. // NOTE: This next line assumes all machines this code runs on support the
  126. // local APIC mapping MSR that PII and newer Intel processors have.
  127. // Code that calls this function MUST properly screen for manufacturer,
  128. // processor family, and local apic support before calling this function.
  129. ApicBase=(CHAR *)(ReadIntelMSR(APICBASE)&~(0xfff));
  130. // Map the physical address to a virtual address.
  131. ApicBase=MapPhysToLinear(ApicBase, 4096, 0);
  132. // Return false if the mapping failed.
  133. if (ApicBase==(CHAR *)(-1) || ApicBase==NULL) {
  134. return FALSE;
  135. }
  136. // Mapping succeeded, so load global interrupt control register locations
  137. // and return TRUE.
  138. ApicPerfInterrupt=(ULONG *)(ApicBase+APICPERF);
  139. ApicTimerInterrupt=(ULONG *)(ApicBase+APICTIMER);
  140. ApicNmiInterrupt=(ULONG *)(ApicBase+APICNMI);
  141. ApicIntrInterrupt=(ULONG *)(ApicBase+APICINTR);
  142. return TRUE;
  143. }
  144. BOOL
  145. MachineHasAPIC (
  146. VOID
  147. )
  148. /*++
  149. Routine Description:
  150. Check the processor manufacturer, family, and the local APIC feature bit, and
  151. then call InitializeAPICBase on supported processors.
  152. Arguments:
  153. None.
  154. Return Value:
  155. FALSE for unsupported processor manufacturers and families and for processors
  156. without the local APIC feature bit set.
  157. If the manufacturer and processor family are supported and the processor sets
  158. the local APIC feature bit, then we call InitializeAPICBase and return the
  159. value returned by that function.
  160. --*/
  161. {
  162. if (CPUManufacturer==INTEL && CPUFamily>=6 && (CPUFeatures&0x20)) {
  163. return InitializeAPICBase();
  164. }
  165. if (CPUManufacturer==AMD && CPUFamily>=6 && (CPUFeatures&0x20)) {
  166. return InitializeAPICBase();
  167. }
  168. return FALSE;
  169. }
  170. #pragma LOCKED_CODE
  171. #pragma LOCKED_DATA
  172. #if 0
  173. __inline
  174. VOID
  175. GenerateLocalHardwareInterrupt (
  176. ULONG interrupt
  177. )
  178. /*++
  179. Routine Description:
  180. This routine can be used to generate what will appear to be a hardware interrupt.
  181. The local APIC is used to send the passed interrupt vector number to itself
  182. and will then handle the interrupt by invoking specified interrupt vector to
  183. handle the interrupt. I do NOT know whether this will work at all. It may or may not
  184. work with interrupt handlers that normally handle interrupts coming from an external
  185. IO APIC, and it may or may not work with interrupt handlers that normally handle
  186. interrupts coming through an external PIC. It seemed like an idea with interesting
  187. possibilities, so I wrote the code. It is NOT currently used. Note that for code
  188. already running in ring 0 on x86 processors, if you just want to run an arbitrary
  189. interrupt handler, it is much faster to just simulate the interrupt by running
  190. an __asm int x instruction.
  191. WARNING: This function does NOT currently wait until the interrupt is delivered
  192. before returning.
  193. Arguments:
  194. interrupt - Contains the interrupt vector number of the interrupt to be simulated.
  195. Return Value:
  196. None.
  197. --*/
  198. {
  199. // First write this machines local apic ID into the destination register of the ICR.
  200. // We must do this with interrupts disabled so that it is safe on NT multiproc
  201. // machines. Otherwise we could read the APIC ID for one processor and be switched
  202. // out and load it into a different processors register.
  203. SaveAndDisableMaskableInterrupts();
  204. WriteAPIC(APICICRHIGH,ReadAPIC(APICID));
  205. WriteAPIC(APICICRLOW,ASSERTIRQ|interrupt);
  206. RestoreMaskableInterrupts();
  207. }
  208. #endif
  209. BOOL
  210. TurnOnLocalApic (
  211. VOID
  212. )
  213. {
  214. if (!(CPUFeatures&0x20)) {
  215. return FALSE;
  216. }
  217. {
  218. ULONGLONG apicbaseaddress;
  219. // Make sure that APIC turned on by MSRs.
  220. apicbaseaddress=ReadIntelMSR(APICBASE);
  221. if (!(apicbaseaddress&0x800)) { // Apic is turned off.
  222. // First disable all interrupts.
  223. SaveAndDisableMaskableInterrupts();
  224. // Try turning it back on. Intel claims this doesn't work. It does.
  225. apicbaseaddress|=0x800;
  226. WriteIntelMSR(APICBASE, apicbaseaddress);
  227. // The following code should only be run in the case when the local APIC was
  228. // not turned on and we then turned it on.
  229. // Now check if the local APIC is turned on. If so, then set it up.
  230. if (ReadIntelMSR(APICBASE)&0x800) {
  231. // Local APIC is on.
  232. // Hook the APIC spurious interrupt vector if we have not hooked it before.
  233. if (*(PULONGLONG)&OriginalApicSpuriousVector==0 &&
  234. HookInterrupt(ApicSpuriousVector, &OriginalApicSpuriousVector, RtpLocalApicSpuriousHandler)!=STATUS_SUCCESS) {
  235. RestoreMaskableInterrupts();
  236. return FALSE;
  237. }
  238. // Now enable the APIC itself.
  239. // This also loads the spurious interrupt vector.
  240. WriteAPIC(APICSPURIOUS,(ReadAPIC(APICSPURIOUS)&0xfffffc00)|0x100|APICSPURIOUSIDTINDEX);
  241. // Now setup INTR.
  242. // Unmasked, ExtINT.
  243. WriteAPIC(APICINTR,(ReadAPIC(APICINTR)&0xfffe58ff)|EXTINT);
  244. // Now setup NMI.
  245. // Masked, NMI.
  246. // Leave external NMI enabled.
  247. WriteAPIC(APICNMI,(ReadAPIC(APICNMI)&0xfffe58ff)|NMI);
  248. // Now reenable interrupts.
  249. RestoreMaskableInterrupts();
  250. return TRUE;
  251. }
  252. else {
  253. // Local APIC could not be turned on with the MSRs!
  254. // This WILL happen on some mobile parts.
  255. // Now reenable interrupts.
  256. RestoreMaskableInterrupts();
  257. dprintf(("RealTime Executive could not enable local APIC. RT NOT RUNNING!"));
  258. return FALSE;
  259. }
  260. }
  261. else { // Local APIC is already turned on!
  262. // This will happen for HALs that use the local APIC. (mp, ACPI)
  263. // We should not touch the spurious, ExtINT, or NMI vectors in this case.
  264. // We do however read the settings out of the local APIC for the local timer
  265. // interrupt vector, and the performance counter interrupt vector. That
  266. // way we use the same vectors as the HAL initially programmed. (Except
  267. // that we do set the NMI flag in the performance counter interrupt, so
  268. // it actually uses interrupt vector 2.)
  269. ApicTimerVector=ReadAPIC(APICTIMER)&VECTORMASK;
  270. #ifdef MASKABLEINTERRUPT
  271. ApicPerfVector=ReadAPIC(APICPERF)&VECTORMASK;
  272. #else
  273. ApicPerfVector=NMI|(ReadAPIC(APICPERF)&VECTORMASK);
  274. #endif
  275. // We also read the error and spurious vector locations since we need
  276. // them when setting up our private IDTs.
  277. ApicErrorVector=ReadAPIC(APICERROR)&VECTORMASK;
  278. ApicSpuriousVector=ReadAPIC(APICSPURIOUS)&VECTORMASK;
  279. // Make sure the vectors we just read are valid. If not, then load them with
  280. // our defaults. These vectors should never be invalid if the local APIC
  281. // was turned on. The only way we will hit this case is if the BIOS turns
  282. // on the local APIC, but then a HAL without local apic support does not
  283. // turn OFF the local apic.
  284. if (!ApicTimerVector) {
  285. Trap();
  286. ApicTimerVector=MASKABLEIDTINDEX;
  287. }
  288. if (!ApicPerfVector) {
  289. Trap();
  290. ApicPerfVector=RTINTERRUPT;
  291. }
  292. if (!ApicErrorVector) {
  293. Trap();
  294. ApicErrorVector=APICERRORIDTINDEX;
  295. }
  296. if (!ApicSpuriousVector) {
  297. Trap();
  298. ApicSpuriousVector=APICSPURIOUSIDTINDEX;
  299. }
  300. return TRUE;
  301. }
  302. }
  303. }
  304. BOOL
  305. EnableAPIC (
  306. VOID
  307. )
  308. /*++
  309. Routine Description:
  310. This routine will enable the local APIC on processors it knows are supported.
  311. We check if the processor manufacturer and family are supported. If so we
  312. call TurnOnLocalApic to enable the local apic on the processor.
  313. Arguments:
  314. None.
  315. Return Value:
  316. FALSE if processor manufacturer and family are not explicitly supported.
  317. If the manufacturer and processor family are supported then we call
  318. TurnOnLocalApic and return the value returned by that function.
  319. --*/
  320. {
  321. // Is manufaturer supported?
  322. switch (CPUManufacturer) {
  323. case INTEL:
  324. // Check the processor family code for Intel.
  325. switch (CPUFamily) {
  326. case 6: // PII, PIII, Celeron
  327. case 0xf: // P4
  328. return TurnOnLocalApic();
  329. break;
  330. default:
  331. break;
  332. }
  333. break;
  334. case AMD:
  335. // Check the processor family code for AMD.
  336. switch (CPUFamily) {
  337. case 6: // Athlon, Duron
  338. return TurnOnLocalApic();
  339. break;
  340. default:
  341. break;
  342. }
  343. break;
  344. default:
  345. break;
  346. }
  347. return FALSE;
  348. }
  349. #pragma PAGEABLE_CODE
  350. #pragma PAGEABLE_DATA