MemDetect.c 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352
  1. /**@file
  2. Memory Detection for Virtual Machines.
  3. Copyright (c) 2006 - 2016, Intel Corporation. All rights reserved.<BR>
  4. Copyright (c) 2019, Citrix Systems, Inc.
  5. SPDX-License-Identifier: BSD-2-Clause-Patent
  6. Module Name:
  7. MemDetect.c
  8. **/
  9. //
  10. // The package level header files this module uses
  11. //
  12. #include <IndustryStandard/Q35MchIch9.h>
  13. #include <PiPei.h>
  14. //
  15. // The Library classes this module consumes
  16. //
  17. #include <Library/BaseLib.h>
  18. #include <Library/BaseMemoryLib.h>
  19. #include <Library/DebugLib.h>
  20. #include <Library/HobLib.h>
  21. #include <Library/IoLib.h>
  22. #include <Library/PcdLib.h>
  23. #include <Library/PciLib.h>
  24. #include <Library/PeimEntryPoint.h>
  25. #include <Library/ResourcePublicationLib.h>
  26. #include "Platform.h"
  27. #include "Cmos.h"
  28. UINT8 mPhysMemAddressWidth;
  29. STATIC UINT32 mS3AcpiReservedMemoryBase;
  30. STATIC UINT32 mS3AcpiReservedMemorySize;
  31. STATIC UINT16 mQ35TsegMbytes;
  32. VOID
  33. Q35TsegMbytesInitialization (
  34. VOID
  35. )
  36. {
  37. UINT16 ExtendedTsegMbytes;
  38. RETURN_STATUS PcdStatus;
  39. if (mHostBridgeDevId != INTEL_Q35_MCH_DEVICE_ID) {
  40. DEBUG ((
  41. DEBUG_ERROR,
  42. "%a: no TSEG (SMRAM) on host bridge DID=0x%04x; "
  43. "only DID=0x%04x (Q35) is supported\n",
  44. __FUNCTION__,
  45. mHostBridgeDevId,
  46. INTEL_Q35_MCH_DEVICE_ID
  47. ));
  48. ASSERT (FALSE);
  49. CpuDeadLoop ();
  50. }
  51. //
  52. // Check if QEMU offers an extended TSEG.
  53. //
  54. // This can be seen from writing MCH_EXT_TSEG_MB_QUERY to the MCH_EXT_TSEG_MB
  55. // register, and reading back the register.
  56. //
  57. // On a QEMU machine type that does not offer an extended TSEG, the initial
  58. // write overwrites whatever value a malicious guest OS may have placed in
  59. // the (unimplemented) register, before entering S3 or rebooting.
  60. // Subsequently, the read returns MCH_EXT_TSEG_MB_QUERY unchanged.
  61. //
  62. // On a QEMU machine type that offers an extended TSEG, the initial write
  63. // triggers an update to the register. Subsequently, the value read back
  64. // (which is guaranteed to differ from MCH_EXT_TSEG_MB_QUERY) tells us the
  65. // number of megabytes.
  66. //
  67. PciWrite16 (DRAMC_REGISTER_Q35 (MCH_EXT_TSEG_MB), MCH_EXT_TSEG_MB_QUERY);
  68. ExtendedTsegMbytes = PciRead16 (DRAMC_REGISTER_Q35 (MCH_EXT_TSEG_MB));
  69. if (ExtendedTsegMbytes == MCH_EXT_TSEG_MB_QUERY) {
  70. mQ35TsegMbytes = PcdGet16 (PcdQ35TsegMbytes);
  71. return;
  72. }
  73. DEBUG ((
  74. DEBUG_INFO,
  75. "%a: QEMU offers an extended TSEG (%d MB)\n",
  76. __FUNCTION__,
  77. ExtendedTsegMbytes
  78. ));
  79. PcdStatus = PcdSet16S (PcdQ35TsegMbytes, ExtendedTsegMbytes);
  80. ASSERT_RETURN_ERROR (PcdStatus);
  81. mQ35TsegMbytes = ExtendedTsegMbytes;
  82. }
  83. STATIC
  84. UINT64
  85. GetHighestSystemMemoryAddress (
  86. BOOLEAN Below4gb
  87. )
  88. {
  89. EFI_E820_ENTRY64 *E820Map;
  90. UINT32 E820EntriesCount;
  91. EFI_E820_ENTRY64 *Entry;
  92. EFI_STATUS Status;
  93. UINT32 Loop;
  94. UINT64 HighestAddress;
  95. UINT64 EntryEnd;
  96. HighestAddress = 0;
  97. Status = XenGetE820Map (&E820Map, &E820EntriesCount);
  98. ASSERT_EFI_ERROR (Status);
  99. for (Loop = 0; Loop < E820EntriesCount; Loop++) {
  100. Entry = E820Map + Loop;
  101. EntryEnd = Entry->BaseAddr + Entry->Length;
  102. if ((Entry->Type == EfiAcpiAddressRangeMemory) &&
  103. (EntryEnd > HighestAddress))
  104. {
  105. if (Below4gb && (EntryEnd <= BASE_4GB)) {
  106. HighestAddress = EntryEnd;
  107. } else if (!Below4gb && (EntryEnd >= BASE_4GB)) {
  108. HighestAddress = EntryEnd;
  109. }
  110. }
  111. }
  112. //
  113. // Round down the end address.
  114. //
  115. return HighestAddress & ~(UINT64)EFI_PAGE_MASK;
  116. }
  117. UINT32
  118. GetSystemMemorySizeBelow4gb (
  119. VOID
  120. )
  121. {
  122. UINT8 Cmos0x34;
  123. UINT8 Cmos0x35;
  124. //
  125. // In PVH case, there is no CMOS, we have to calculate the memory size
  126. // from parsing the E820
  127. //
  128. if (XenPvhDetected ()) {
  129. UINT64 HighestAddress;
  130. HighestAddress = GetHighestSystemMemoryAddress (TRUE);
  131. ASSERT (HighestAddress > 0 && HighestAddress <= BASE_4GB);
  132. return (UINT32)HighestAddress;
  133. }
  134. //
  135. // CMOS 0x34/0x35 specifies the system memory above 16 MB.
  136. // * CMOS(0x35) is the high byte
  137. // * CMOS(0x34) is the low byte
  138. // * The size is specified in 64kb chunks
  139. // * Since this is memory above 16MB, the 16MB must be added
  140. // into the calculation to get the total memory size.
  141. //
  142. Cmos0x34 = (UINT8)CmosRead8 (0x34);
  143. Cmos0x35 = (UINT8)CmosRead8 (0x35);
  144. return (UINT32)(((UINTN)((Cmos0x35 << 8) + Cmos0x34) << 16) + SIZE_16MB);
  145. }
  146. /**
  147. Initialize the mPhysMemAddressWidth variable, based on CPUID data.
  148. **/
  149. VOID
  150. AddressWidthInitialization (
  151. VOID
  152. )
  153. {
  154. UINT32 RegEax;
  155. AsmCpuid (0x80000000, &RegEax, NULL, NULL, NULL);
  156. if (RegEax >= 0x80000008) {
  157. AsmCpuid (0x80000008, &RegEax, NULL, NULL, NULL);
  158. mPhysMemAddressWidth = (UINT8)RegEax;
  159. } else {
  160. mPhysMemAddressWidth = 36;
  161. }
  162. //
  163. // IA-32e paging translates 48-bit linear addresses to 52-bit physical addresses.
  164. //
  165. ASSERT (mPhysMemAddressWidth <= 52);
  166. if (mPhysMemAddressWidth > 48) {
  167. mPhysMemAddressWidth = 48;
  168. }
  169. }
  170. /**
  171. Calculate the cap for the permanent PEI memory.
  172. **/
  173. STATIC
  174. UINT32
  175. GetPeiMemoryCap (
  176. VOID
  177. )
  178. {
  179. BOOLEAN Page1GSupport;
  180. UINT32 RegEax;
  181. UINT32 RegEdx;
  182. UINT32 Pml4Entries;
  183. UINT32 PdpEntries;
  184. UINTN TotalPages;
  185. //
  186. // If DXE is 32-bit, then just return the traditional 64 MB cap.
  187. //
  188. #ifdef MDE_CPU_IA32
  189. if (!FeaturePcdGet (PcdDxeIplSwitchToLongMode)) {
  190. return SIZE_64MB;
  191. }
  192. #endif
  193. //
  194. // Dependent on physical address width, PEI memory allocations can be
  195. // dominated by the page tables built for 64-bit DXE. So we key the cap off
  196. // of those. The code below is based on CreateIdentityMappingPageTables() in
  197. // "MdeModulePkg/Core/DxeIplPeim/X64/VirtualMemory.c".
  198. //
  199. Page1GSupport = FALSE;
  200. if (PcdGetBool (PcdUse1GPageTable)) {
  201. AsmCpuid (0x80000000, &RegEax, NULL, NULL, NULL);
  202. if (RegEax >= 0x80000001) {
  203. AsmCpuid (0x80000001, NULL, NULL, NULL, &RegEdx);
  204. if ((RegEdx & BIT26) != 0) {
  205. Page1GSupport = TRUE;
  206. }
  207. }
  208. }
  209. if (mPhysMemAddressWidth <= 39) {
  210. Pml4Entries = 1;
  211. PdpEntries = 1 << (mPhysMemAddressWidth - 30);
  212. ASSERT (PdpEntries <= 0x200);
  213. } else {
  214. Pml4Entries = 1 << (mPhysMemAddressWidth - 39);
  215. ASSERT (Pml4Entries <= 0x200);
  216. PdpEntries = 512;
  217. }
  218. TotalPages = Page1GSupport ? Pml4Entries + 1 :
  219. (PdpEntries + 1) * Pml4Entries + 1;
  220. ASSERT (TotalPages <= 0x40201);
  221. //
  222. // Add 64 MB for miscellaneous allocations. Note that for
  223. // mPhysMemAddressWidth values close to 36, the cap will actually be
  224. // dominated by this increment.
  225. //
  226. return (UINT32)(EFI_PAGES_TO_SIZE (TotalPages) + SIZE_64MB);
  227. }
  228. /**
  229. Publish PEI core memory
  230. @return EFI_SUCCESS The PEIM initialized successfully.
  231. **/
  232. EFI_STATUS
  233. PublishPeiMemory (
  234. VOID
  235. )
  236. {
  237. EFI_STATUS Status;
  238. EFI_PHYSICAL_ADDRESS MemoryBase;
  239. UINT64 MemorySize;
  240. UINT32 LowerMemorySize;
  241. UINT32 PeiMemoryCap;
  242. LowerMemorySize = GetSystemMemorySizeBelow4gb ();
  243. if (mBootMode == BOOT_ON_S3_RESUME) {
  244. MemoryBase = mS3AcpiReservedMemoryBase;
  245. MemorySize = mS3AcpiReservedMemorySize;
  246. } else {
  247. PeiMemoryCap = GetPeiMemoryCap ();
  248. DEBUG ((
  249. DEBUG_INFO,
  250. "%a: mPhysMemAddressWidth=%d PeiMemoryCap=%u KB\n",
  251. __FUNCTION__,
  252. mPhysMemAddressWidth,
  253. PeiMemoryCap >> 10
  254. ));
  255. //
  256. // Determine the range of memory to use during PEI
  257. //
  258. MemoryBase =
  259. PcdGet32 (PcdOvmfDxeMemFvBase) + PcdGet32 (PcdOvmfDxeMemFvSize);
  260. MemorySize = LowerMemorySize - MemoryBase;
  261. if (MemorySize > PeiMemoryCap) {
  262. MemoryBase = LowerMemorySize - PeiMemoryCap;
  263. MemorySize = PeiMemoryCap;
  264. }
  265. }
  266. //
  267. // Publish this memory to the PEI Core
  268. //
  269. Status = PublishSystemMemory (MemoryBase, MemorySize);
  270. ASSERT_EFI_ERROR (Status);
  271. return Status;
  272. }
  273. /**
  274. Publish system RAM and reserve memory regions
  275. **/
  276. VOID
  277. InitializeRamRegions (
  278. VOID
  279. )
  280. {
  281. XenPublishRamRegions ();
  282. if (mBootMode != BOOT_ON_S3_RESUME) {
  283. //
  284. // Reserve the lock box storage area
  285. //
  286. // Since this memory range will be used on S3 resume, it must be
  287. // reserved as ACPI NVS.
  288. //
  289. // If S3 is unsupported, then various drivers might still write to the
  290. // LockBox area. We ought to prevent DXE from serving allocation requests
  291. // such that they would overlap the LockBox storage.
  292. //
  293. ZeroMem (
  294. (VOID *)(UINTN)PcdGet32 (PcdOvmfLockBoxStorageBase),
  295. (UINTN)PcdGet32 (PcdOvmfLockBoxStorageSize)
  296. );
  297. BuildMemoryAllocationHob (
  298. (EFI_PHYSICAL_ADDRESS)(UINTN)PcdGet32 (PcdOvmfLockBoxStorageBase),
  299. (UINT64)(UINTN)PcdGet32 (PcdOvmfLockBoxStorageSize),
  300. EfiBootServicesData
  301. );
  302. }
  303. }