MemDetect.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372
  1. /**@file
  2. Memory Detection for Virtual Machines.
  3. Copyright (c) 2006 - 2016, Intel Corporation. All rights reserved.<BR>
  4. SPDX-License-Identifier: BSD-2-Clause-Patent
  5. Module Name:
  6. MemDetect.c
  7. **/
  8. //
  9. // The package level header files this module uses
  10. //
  11. #include <IndustryStandard/E820.h>
  12. #include <IndustryStandard/I440FxPiix4.h>
  13. #include <IndustryStandard/Q35MchIch9.h>
  14. #include <IndustryStandard/CloudHv.h>
  15. #include <IndustryStandard/Xen/arch-x86/hvm/start_info.h>
  16. #include <PiPei.h>
  17. #include <Register/Intel/SmramSaveStateMap.h>
  18. //
  19. // The Library classes this module consumes
  20. //
  21. #include <Library/BaseLib.h>
  22. #include <Library/BaseMemoryLib.h>
  23. #include <Library/DebugLib.h>
  24. #include <Library/HobLib.h>
  25. #include <Library/IoLib.h>
  26. #include <Library/MemEncryptSevLib.h>
  27. #include <Library/PcdLib.h>
  28. #include <Library/PciLib.h>
  29. #include <Library/PeimEntryPoint.h>
  30. #include <Library/ResourcePublicationLib.h>
  31. #include <Library/QemuFwCfgLib.h>
  32. #include <Library/QemuFwCfgSimpleParserLib.h>
  33. #include "Platform.h"
  34. VOID
  35. Q35TsegMbytesInitialization (
  36. IN OUT EFI_HOB_PLATFORM_INFO *PlatformInfoHob
  37. )
  38. {
  39. UINT16 ExtendedTsegMbytes;
  40. RETURN_STATUS PcdStatus;
  41. ASSERT (PlatformInfoHob->HostBridgeDevId == INTEL_Q35_MCH_DEVICE_ID);
  42. //
  43. // Check if QEMU offers an extended TSEG.
  44. //
  45. // This can be seen from writing MCH_EXT_TSEG_MB_QUERY to the MCH_EXT_TSEG_MB
  46. // register, and reading back the register.
  47. //
  48. // On a QEMU machine type that does not offer an extended TSEG, the initial
  49. // write overwrites whatever value a malicious guest OS may have placed in
  50. // the (unimplemented) register, before entering S3 or rebooting.
  51. // Subsequently, the read returns MCH_EXT_TSEG_MB_QUERY unchanged.
  52. //
  53. // On a QEMU machine type that offers an extended TSEG, the initial write
  54. // triggers an update to the register. Subsequently, the value read back
  55. // (which is guaranteed to differ from MCH_EXT_TSEG_MB_QUERY) tells us the
  56. // number of megabytes.
  57. //
  58. PciWrite16 (DRAMC_REGISTER_Q35 (MCH_EXT_TSEG_MB), MCH_EXT_TSEG_MB_QUERY);
  59. ExtendedTsegMbytes = PciRead16 (DRAMC_REGISTER_Q35 (MCH_EXT_TSEG_MB));
  60. if (ExtendedTsegMbytes == MCH_EXT_TSEG_MB_QUERY) {
  61. PlatformInfoHob->Q35TsegMbytes = PcdGet16 (PcdQ35TsegMbytes);
  62. return;
  63. }
  64. DEBUG ((
  65. DEBUG_INFO,
  66. "%a: QEMU offers an extended TSEG (%d MB)\n",
  67. __FUNCTION__,
  68. ExtendedTsegMbytes
  69. ));
  70. PcdStatus = PcdSet16S (PcdQ35TsegMbytes, ExtendedTsegMbytes);
  71. ASSERT_RETURN_ERROR (PcdStatus);
  72. PlatformInfoHob->Q35TsegMbytes = ExtendedTsegMbytes;
  73. }
  74. VOID
  75. Q35SmramAtDefaultSmbaseInitialization (
  76. IN OUT EFI_HOB_PLATFORM_INFO *PlatformInfoHob
  77. )
  78. {
  79. RETURN_STATUS PcdStatus;
  80. ASSERT (PlatformInfoHob->HostBridgeDevId == INTEL_Q35_MCH_DEVICE_ID);
  81. PlatformInfoHob->Q35SmramAtDefaultSmbase = FALSE;
  82. if (FeaturePcdGet (PcdCsmEnable)) {
  83. DEBUG ((
  84. DEBUG_INFO,
  85. "%a: SMRAM at default SMBASE not checked due to CSM\n",
  86. __FUNCTION__
  87. ));
  88. } else {
  89. UINTN CtlReg;
  90. UINT8 CtlRegVal;
  91. CtlReg = DRAMC_REGISTER_Q35 (MCH_DEFAULT_SMBASE_CTL);
  92. PciWrite8 (CtlReg, MCH_DEFAULT_SMBASE_QUERY);
  93. CtlRegVal = PciRead8 (CtlReg);
  94. PlatformInfoHob->Q35SmramAtDefaultSmbase = (BOOLEAN)(CtlRegVal ==
  95. MCH_DEFAULT_SMBASE_IN_RAM);
  96. DEBUG ((
  97. DEBUG_INFO,
  98. "%a: SMRAM at default SMBASE %a\n",
  99. __FUNCTION__,
  100. PlatformInfoHob->Q35SmramAtDefaultSmbase ? "found" : "not found"
  101. ));
  102. }
  103. PcdStatus = PcdSetBoolS (
  104. PcdQ35SmramAtDefaultSmbase,
  105. PlatformInfoHob->Q35SmramAtDefaultSmbase
  106. );
  107. ASSERT_RETURN_ERROR (PcdStatus);
  108. }
  109. /**
  110. Initialize the PhysMemAddressWidth field in PlatformInfoHob based on guest RAM size.
  111. **/
  112. VOID
  113. AddressWidthInitialization (
  114. IN OUT EFI_HOB_PLATFORM_INFO *PlatformInfoHob
  115. )
  116. {
  117. RETURN_STATUS PcdStatus;
  118. PlatformAddressWidthInitialization (PlatformInfoHob);
  119. //
  120. // If DXE is 32-bit, then we're done; PciBusDxe will degrade 64-bit MMIO
  121. // resources to 32-bit anyway. See DegradeResource() in
  122. // "PciResourceSupport.c".
  123. //
  124. #ifdef MDE_CPU_IA32
  125. if (!FeaturePcdGet (PcdDxeIplSwitchToLongMode)) {
  126. return;
  127. }
  128. #endif
  129. if (PlatformInfoHob->PcdPciMmio64Size == 0) {
  130. if (PlatformInfoHob->BootMode != BOOT_ON_S3_RESUME) {
  131. DEBUG ((
  132. DEBUG_INFO,
  133. "%a: disabling 64-bit PCI host aperture\n",
  134. __FUNCTION__
  135. ));
  136. PcdStatus = PcdSet64S (PcdPciMmio64Size, 0);
  137. ASSERT_RETURN_ERROR (PcdStatus);
  138. }
  139. return;
  140. }
  141. if (PlatformInfoHob->BootMode != BOOT_ON_S3_RESUME) {
  142. //
  143. // The core PciHostBridgeDxe driver will automatically add this range to
  144. // the GCD memory space map through our PciHostBridgeLib instance; here we
  145. // only need to set the PCDs.
  146. //
  147. PcdStatus = PcdSet64S (PcdPciMmio64Base, PlatformInfoHob->PcdPciMmio64Base);
  148. ASSERT_RETURN_ERROR (PcdStatus);
  149. PcdStatus = PcdSet64S (PcdPciMmio64Size, PlatformInfoHob->PcdPciMmio64Size);
  150. ASSERT_RETURN_ERROR (PcdStatus);
  151. DEBUG ((
  152. DEBUG_INFO,
  153. "%a: Pci64Base=0x%Lx Pci64Size=0x%Lx\n",
  154. __FUNCTION__,
  155. PlatformInfoHob->PcdPciMmio64Base,
  156. PlatformInfoHob->PcdPciMmio64Size
  157. ));
  158. }
  159. }
  160. /**
  161. Calculate the cap for the permanent PEI memory.
  162. **/
  163. STATIC
  164. UINT32
  165. GetPeiMemoryCap (
  166. IN EFI_HOB_PLATFORM_INFO *PlatformInfoHob
  167. )
  168. {
  169. BOOLEAN Page1GSupport;
  170. UINT32 RegEax;
  171. UINT32 RegEdx;
  172. UINT32 Pml4Entries;
  173. UINT32 PdpEntries;
  174. UINTN TotalPages;
  175. //
  176. // If DXE is 32-bit, then just return the traditional 64 MB cap.
  177. //
  178. #ifdef MDE_CPU_IA32
  179. if (!FeaturePcdGet (PcdDxeIplSwitchToLongMode)) {
  180. return SIZE_64MB;
  181. }
  182. #endif
  183. //
  184. // Dependent on physical address width, PEI memory allocations can be
  185. // dominated by the page tables built for 64-bit DXE. So we key the cap off
  186. // of those. The code below is based on CreateIdentityMappingPageTables() in
  187. // "MdeModulePkg/Core/DxeIplPeim/X64/VirtualMemory.c".
  188. //
  189. Page1GSupport = FALSE;
  190. if (PcdGetBool (PcdUse1GPageTable)) {
  191. AsmCpuid (0x80000000, &RegEax, NULL, NULL, NULL);
  192. if (RegEax >= 0x80000001) {
  193. AsmCpuid (0x80000001, NULL, NULL, NULL, &RegEdx);
  194. if ((RegEdx & BIT26) != 0) {
  195. Page1GSupport = TRUE;
  196. }
  197. }
  198. }
  199. if (PlatformInfoHob->PhysMemAddressWidth <= 39) {
  200. Pml4Entries = 1;
  201. PdpEntries = 1 << (PlatformInfoHob->PhysMemAddressWidth - 30);
  202. ASSERT (PdpEntries <= 0x200);
  203. } else {
  204. if (PlatformInfoHob->PhysMemAddressWidth > 48) {
  205. Pml4Entries = 0x200;
  206. } else {
  207. Pml4Entries = 1 << (PlatformInfoHob->PhysMemAddressWidth - 39);
  208. }
  209. ASSERT (Pml4Entries <= 0x200);
  210. PdpEntries = 512;
  211. }
  212. TotalPages = Page1GSupport ? Pml4Entries + 1 :
  213. (PdpEntries + 1) * Pml4Entries + 1;
  214. ASSERT (TotalPages <= 0x40201);
  215. //
  216. // Add 64 MB for miscellaneous allocations. Note that for
  217. // PhysMemAddressWidth values close to 36, the cap will actually be
  218. // dominated by this increment.
  219. //
  220. return (UINT32)(EFI_PAGES_TO_SIZE (TotalPages) + SIZE_64MB);
  221. }
  222. /**
  223. Publish PEI core memory
  224. @return EFI_SUCCESS The PEIM initialized successfully.
  225. **/
  226. EFI_STATUS
  227. PublishPeiMemory (
  228. IN OUT EFI_HOB_PLATFORM_INFO *PlatformInfoHob
  229. )
  230. {
  231. EFI_STATUS Status;
  232. EFI_PHYSICAL_ADDRESS MemoryBase;
  233. UINT64 MemorySize;
  234. UINT32 LowerMemorySize;
  235. UINT32 PeiMemoryCap;
  236. UINT32 S3AcpiReservedMemoryBase;
  237. UINT32 S3AcpiReservedMemorySize;
  238. PlatformGetSystemMemorySizeBelow4gb (PlatformInfoHob);
  239. LowerMemorySize = PlatformInfoHob->LowMemory;
  240. if (PlatformInfoHob->SmmSmramRequire) {
  241. //
  242. // TSEG is chipped from the end of low RAM
  243. //
  244. LowerMemorySize -= PlatformInfoHob->Q35TsegMbytes * SIZE_1MB;
  245. }
  246. S3AcpiReservedMemoryBase = 0;
  247. S3AcpiReservedMemorySize = 0;
  248. //
  249. // If S3 is supported, then the S3 permanent PEI memory is placed next,
  250. // downwards. Its size is primarily dictated by CpuMpPei. The formula below
  251. // is an approximation.
  252. //
  253. if (PlatformInfoHob->S3Supported) {
  254. S3AcpiReservedMemorySize = SIZE_512KB +
  255. PlatformInfoHob->PcdCpuMaxLogicalProcessorNumber *
  256. PcdGet32 (PcdCpuApStackSize);
  257. S3AcpiReservedMemoryBase = LowerMemorySize - S3AcpiReservedMemorySize;
  258. LowerMemorySize = S3AcpiReservedMemoryBase;
  259. }
  260. PlatformInfoHob->S3AcpiReservedMemoryBase = S3AcpiReservedMemoryBase;
  261. PlatformInfoHob->S3AcpiReservedMemorySize = S3AcpiReservedMemorySize;
  262. if (PlatformInfoHob->BootMode == BOOT_ON_S3_RESUME) {
  263. MemoryBase = S3AcpiReservedMemoryBase;
  264. MemorySize = S3AcpiReservedMemorySize;
  265. } else {
  266. PeiMemoryCap = GetPeiMemoryCap (PlatformInfoHob);
  267. DEBUG ((
  268. DEBUG_INFO,
  269. "%a: PhysMemAddressWidth=%d PeiMemoryCap=%u KB\n",
  270. __FUNCTION__,
  271. PlatformInfoHob->PhysMemAddressWidth,
  272. PeiMemoryCap >> 10
  273. ));
  274. //
  275. // Determine the range of memory to use during PEI
  276. //
  277. // Technically we could lay the permanent PEI RAM over SEC's temporary
  278. // decompression and scratch buffer even if "secure S3" is needed, since
  279. // their lifetimes don't overlap. However, PeiFvInitialization() will cover
  280. // RAM up to PcdOvmfDecompressionScratchEnd with an EfiACPIMemoryNVS memory
  281. // allocation HOB, and other allocations served from the permanent PEI RAM
  282. // shouldn't overlap with that HOB.
  283. //
  284. MemoryBase = PlatformInfoHob->S3Supported && PlatformInfoHob->SmmSmramRequire ?
  285. PcdGet32 (PcdOvmfDecompressionScratchEnd) :
  286. PcdGet32 (PcdOvmfDxeMemFvBase) + PcdGet32 (PcdOvmfDxeMemFvSize);
  287. MemorySize = LowerMemorySize - MemoryBase;
  288. if (MemorySize > PeiMemoryCap) {
  289. MemoryBase = LowerMemorySize - PeiMemoryCap;
  290. MemorySize = PeiMemoryCap;
  291. }
  292. }
  293. //
  294. // MEMFD_BASE_ADDRESS separates the SMRAM at the default SMBASE from the
  295. // normal boot permanent PEI RAM. Regarding the S3 boot path, the S3
  296. // permanent PEI RAM is located even higher.
  297. //
  298. if (PlatformInfoHob->SmmSmramRequire && PlatformInfoHob->Q35SmramAtDefaultSmbase) {
  299. ASSERT (SMM_DEFAULT_SMBASE + MCH_DEFAULT_SMBASE_SIZE <= MemoryBase);
  300. }
  301. //
  302. // Publish this memory to the PEI Core
  303. //
  304. Status = PublishSystemMemory (MemoryBase, MemorySize);
  305. ASSERT_EFI_ERROR (Status);
  306. return Status;
  307. }
  308. /**
  309. Publish system RAM and reserve memory regions
  310. **/
  311. VOID
  312. InitializeRamRegions (
  313. IN EFI_HOB_PLATFORM_INFO *PlatformInfoHob
  314. )
  315. {
  316. if (TdIsEnabled ()) {
  317. PlatformTdxPublishRamRegions ();
  318. return;
  319. }
  320. PlatformQemuInitializeRam (PlatformInfoHob);
  321. SevInitializeRam ();
  322. PlatformQemuInitializeRamForS3 (PlatformInfoHob);
  323. }