Leaked source code of windows server 2003
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.

445 lines
12 KiB

  1. /*++
  2. Copyright (c) 2000 Microsoft Corporation
  3. Module Name:
  4. mpclock.c
  5. Abstract:
  6. This module implements processor starup code.
  7. Author:
  8. Forrest Foltz (forrestf) 27-Oct-2000
  9. Environment:
  10. Kernel mode only.
  11. Revision History:
  12. --*/
  13. #include "halcmn.h"
  14. #include <acpitabl.h>
  15. #include <xxacpi.h>
  16. #include <ixsleep.h>
  17. #if !defined(NT_UP)
  18. //
  19. // Pull in the real- and 32-bit protected-mode object code
  20. //
  21. #include "pmstub.h"
  22. #include "rmstub.h"
  23. extern UCHAR HalpLMStub[];
  24. extern UCHAR HalpLMIdentityStub[];
  25. extern UCHAR HalpLMIdentityStubEnd[];
  26. #define HALP_LMIDENTITYSTUB_LENGTH (HalpLMIdentityStubEnd - HalpLMIdentityStub)
  27. #endif
  28. extern BOOLEAN HalpHiberInProgress;
  29. extern PUCHAR Halp1stPhysicalPageVaddr;
  30. #define WARM_RESET_VECTOR 0x467 // warm reset vector in ROM data segment
  31. #define CMOS_SHUTDOWN_REG 0x0f
  32. #define CMOS_SHUTDOWN_JMP 0x0a
  33. #define _20BITS (1 << 20)
  34. ULONG
  35. HalpStartProcessor (
  36. IN PVOID InitCodePhysAddr,
  37. IN ULONG ProcessorNumber
  38. );
  39. VOID
  40. HalpBuildKGDTEntry32 (
  41. IN PKGDTENTRY64 Gdt,
  42. IN ULONG Selector,
  43. IN ULONG Base,
  44. IN ULONG Limit,
  45. IN ULONG Type,
  46. IN BOOLEAN LongMode
  47. );
  48. BOOLEAN
  49. HalStartNextProcessor (
  50. IN PLOADER_PARAMETER_BLOCK LoaderBlock,
  51. IN PKPROCESSOR_STATE ProcessorState
  52. )
  53. /*++
  54. Routine Description:
  55. This routine is called by the kernel during kernel initialization to
  56. obtain more processors. It is called until no more processors are
  57. available.
  58. If another processor exists this function is to initialize it to the
  59. passed in processor state structure, and return TRUE.
  60. If another processor does not exist or if the processor fails to start,
  61. then FALSE is returned.
  62. Also note that the loader block has been set up for the next processor.
  63. The new processor logical thread number can be obtained from it if
  64. required.
  65. In order to use the Startup IPI the real mode startup code must be page
  66. aligned. The HalpLowStubPhysicalAddress has always been page aligned
  67. but because the PxParamBlock was placed first in this segment the real
  68. mode code has been something other than page aligned. THis has been
  69. changed by making the first entry in the PxParamBlock a jump instruction
  70. to the real mode startup code.
  71. Arguments:
  72. LoaderBlock - A pointer to the loader block which has been initialized
  73. for the next processor.
  74. ProcessorState - A pointer to a structure which containts the initial
  75. state of the processor.
  76. Return Value:
  77. TRUE - ProcessorNumber was dispatched.
  78. FALSE - A processor was not dispatched, or no other processor exists.
  79. --*/
  80. {
  81. #if defined(NT_UP)
  82. return FALSE;
  83. #else
  84. ULONG cr3;
  85. PPROCESSOR_START_BLOCK startupBlock;
  86. ULONG __unaligned *resetVectorLocation;
  87. ULONG oldResetVector;
  88. ULONG newResetVector;
  89. UCHAR cmosValue;
  90. PKPRCB prcb;
  91. ULONG apicId;
  92. ULONG count;
  93. PVOID pmStubStart;
  94. ULONG startupBlockPhysical;
  95. PVOID pmStub;
  96. PCHAR dst;
  97. BOOLEAN processorStarted;
  98. C_ASSERT(PSB_GDT32_CODE64 == KGDT64_R0_CODE);
  99. //
  100. // Assume failure.
  101. //
  102. processorStarted = FALSE;
  103. //
  104. // Initialize the startup block. First, copy the x86 real-mode
  105. // code, the protected-mode 32-bit code and a bit of 64-bit long
  106. // mode code into the startup block.
  107. //
  108. // The structure that is ultimately assembled by this code is laid
  109. // out as follows.
  110. //
  111. /*
  112. +========================================================+ <- HalpLowStub
  113. | Jmp instruction to end of PROCESSOR_START_BLOCK | >--+
  114. +--------------------------------------------------------+ |
  115. | | |
  116. | Remainder of PROCESSOR_START_BLOCK fields | |
  117. | | |
  118. +--------------------------------------------------------+ |
  119. | | <--+
  120. | 16-bit real-mode code from StartPx_RMStub (xmstub.asm) |
  121. | |
  122. | Enters 32-bit protected mode (no paging), performs a |
  123. | far jump to StartPx_PMStub. |
  124. | | >--+
  125. +--------------------------------------------------------+ |
  126. | | <--+
  127. | 32-bit protected-mode code from StartPx_PMStub. |
  128. | |
  129. | Enters 64-bit long mode using the identity-mapped |
  130. | CR3 found in PROCESSOR_START_BLOCK. Performs a 32-bit |
  131. | far jump to HalpLMIdentityStub. |
  132. | | >--+
  133. +--------------------------------------------------------+ |
  134. | | <--+
  135. | 64-bit long mode code from HalpLMIdentityStub |
  136. | (amd64s.asm). This performs a long (64-bit) jump to |
  137. | HalpLMStub (amd64s.asm). This code exists only because|
  138. | HalpLMStub resides at an address that is inaccessible |
  139. | via a 32-bit far jump. |
  140. | | -> HalpLMStub()
  141. +========================================================+
  142. */
  143. //
  144. // Copy HalpRMStub
  145. //
  146. startupBlock = (PPROCESSOR_START_BLOCK)HalpLowStub;
  147. startupBlockPhysical = PtrToUlong(HalpLowStubPhysicalAddress);
  148. dst = (PCHAR)startupBlock;
  149. RtlCopyMemory(dst, HalpRMStub, HalpRMStubSize);
  150. //
  151. // Copy HalpPMStub
  152. //
  153. dst += HalpRMStubSize;
  154. RtlCopyMemory(dst, HalpPMStub, HalpPMStubSize);
  155. startupBlock->PmTarget.Selector = PSB_GDT32_CODE32;
  156. startupBlock->PmTarget.Offset =
  157. (ULONG)(dst - (PUCHAR)startupBlock) + startupBlockPhysical;
  158. //
  159. // Copy HalpLMIdentityStub
  160. //
  161. dst += HalpPMStubSize;
  162. RtlCopyMemory(dst, HalpLMIdentityStub, HALP_LMIDENTITYSTUB_LENGTH);
  163. startupBlock->LmIdentityTarget.Selector = PSB_GDT32_CODE64;
  164. startupBlock->LmIdentityTarget.Offset =
  165. (ULONG)(dst - (PUCHAR)startupBlock) + startupBlockPhysical;
  166. //
  167. // Now begin filling in other startup block fields
  168. //
  169. startupBlock->SelfMap = startupBlock;
  170. startupBlock->LmTarget = HalpLMStub;
  171. //
  172. // Build the temporary GDT entries to be used while in 32-bit
  173. // protected mode
  174. //
  175. HalpBuildKGDTEntry32(startupBlock->Gdt,
  176. PSB_GDT32_CODE32,
  177. 0,
  178. (ULONG)(-1),
  179. TYPE_CODE,
  180. FALSE);
  181. HalpBuildKGDTEntry32(startupBlock->Gdt,
  182. PSB_GDT32_DATA32,
  183. 0,
  184. (ULONG)(-1),
  185. TYPE_DATA,
  186. FALSE);
  187. //
  188. // Build the temporary code selector GDT entry to be used while in long
  189. // mode.
  190. //
  191. HalpBuildKGDTEntry32(startupBlock->Gdt,
  192. PSB_GDT32_CODE64,
  193. 0, // base and limit are ignored in
  194. 0, // a long-mode CS selector
  195. TYPE_CODE,
  196. TRUE);
  197. //
  198. // Build the pseudo-descriptor for the GDT
  199. //
  200. startupBlock->Gdt32.Limit = sizeof(startupBlock->Gdt) - 1;
  201. startupBlock->Gdt32.Base =
  202. startupBlockPhysical + FIELD_OFFSET(PROCESSOR_START_BLOCK,Gdt);
  203. //
  204. // Build a CR3 for the starting processor. If returning from
  205. // hibernation, then use setup tiled CR3 else create a new map.
  206. //
  207. // Also, record the PAT of the current processor
  208. //
  209. if (HalpHiberInProgress == FALSE) {
  210. startupBlock->TiledCr3 = HalpBuildTiledCR3(ProcessorState);
  211. } else {
  212. startupBlock->TiledCr3 = CurTiledCr3LowPart;
  213. }
  214. startupBlock->MsrPat = ReadMSR(MSR_PAT);
  215. //
  216. // Copy in the processor state and the linear address of the startup
  217. // block, and zero the completionflag.
  218. //
  219. startupBlock->ProcessorState = *ProcessorState;
  220. startupBlock->CompletionFlag = 0;
  221. //
  222. // The reset vector lives in the BIOS data area. Build a pointer to it
  223. // and store the existing value locally.
  224. //
  225. resetVectorLocation = (PULONG)((PUCHAR)Halp1stPhysicalPageVaddr +
  226. WARM_RESET_VECTOR);
  227. oldResetVector = *resetVectorLocation;
  228. //
  229. // Build the new real-mode vector in SEG:OFFS format and store it in the
  230. // BIOS data area.
  231. //
  232. newResetVector = PtrToUlong(HalpLowStubPhysicalAddress);
  233. newResetVector <<= 12;
  234. *resetVectorLocation = newResetVector;
  235. //
  236. // Tell the BIOS to jump via the vector we gave it by setting the
  237. // reset code in the cmos
  238. //
  239. HalpAcquireCmosSpinLock();
  240. cmosValue = CMOS_READ(CMOS_SHUTDOWN_REG);
  241. CMOS_WRITE(CMOS_SHUTDOWN_REG,CMOS_SHUTDOWN_JMP);
  242. HalpReleaseCmosSpinLock();
  243. prcb = (PKPRCB)LoaderBlock->Prcb;
  244. apicId = HalpStartProcessor(HalpLowStubPhysicalAddress, prcb->Number);
  245. if (apicId == 0) {
  246. //
  247. // The processor could not be started.
  248. //
  249. goto procStartCleanup;
  250. }
  251. //
  252. // Set the apic ID in the target processor's PRCB and wait for it to
  253. // signal that it has started.
  254. //
  255. apicId -= 1;
  256. ((PHALPCR)&prcb->HalReserved)->ApicId = apicId;
  257. count = 0;
  258. while (TRUE) {
  259. if (startupBlock->CompletionFlag != 0) {
  260. break;
  261. }
  262. if (count == 200) {
  263. goto procStartCleanup;
  264. }
  265. KeStallExecutionProcessor(2000);
  266. count += 1;
  267. }
  268. HalpMarkProcessorStarted(apicId, prcb->Number);
  269. processorStarted = TRUE;
  270. procStartCleanup:
  271. //
  272. // Free the identity mapping structures, restore the CMOS reset vector
  273. // and method, and return
  274. //
  275. HalpFreeTiledCR3();
  276. *resetVectorLocation = oldResetVector;
  277. HalpAcquireCmosSpinLock();
  278. CMOS_WRITE(CMOS_SHUTDOWN_REG,cmosValue);
  279. HalpReleaseCmosSpinLock();
  280. return processorStarted;
  281. #endif // NT_UP
  282. }
  283. VOID
  284. HalpBuildKGDTEntry32 (
  285. IN PKGDTENTRY64 Gdt,
  286. IN ULONG Selector,
  287. IN ULONG Base,
  288. IN ULONG Limit,
  289. IN ULONG Type,
  290. IN BOOLEAN LongMode
  291. )
  292. {
  293. KGDT_BASE base;
  294. KGDT_LIMIT limit;
  295. PKGDTENTRY64 gdtEntry;
  296. gdtEntry = &Gdt[Selector >> 4];
  297. //
  298. // Note that although gdtEntry points to a 16-byte structure,
  299. // we're actually building an 8-byte GDT so we are careful to not
  300. // touch the high 8 bytes.
  301. //
  302. RtlZeroMemory(gdtEntry, 8);
  303. //
  304. // Set limit information
  305. //
  306. if (Limit > (_20BITS - 1)) {
  307. gdtEntry->Bits.Granularity = GRANULARITY_PAGE;
  308. limit.Limit = Limit / PAGE_SIZE;
  309. } else {
  310. limit.Limit = Limit;
  311. }
  312. gdtEntry->LimitLow = limit.LimitLow;
  313. gdtEntry->Bits.LimitHigh = limit.LimitHigh;
  314. //
  315. // Set base information
  316. //
  317. base.Base = Base;
  318. gdtEntry->BaseLow = base.BaseLow;
  319. gdtEntry->Bits.BaseMiddle = base.BaseMiddle;
  320. gdtEntry->Bits.BaseHigh = base.BaseHigh;
  321. //
  322. // Set other bits
  323. //
  324. gdtEntry->Bits.Present = 1;
  325. gdtEntry->Bits.Dpl = DPL_SYSTEM;
  326. if (LongMode == FALSE) {
  327. gdtEntry->Bits.DefaultBig = 1;
  328. }
  329. gdtEntry->Bits.Type = Type;
  330. if (LongMode != FALSE) {
  331. gdtEntry->Bits.LongMode = 1;
  332. }
  333. }