SmmCpuMemoryManagement.c 61 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904
  1. /** @file
  2. Copyright (c) 2016 - 2019, Intel Corporation. All rights reserved.<BR>
  3. SPDX-License-Identifier: BSD-2-Clause-Patent
  4. **/
  5. #include "PiSmmCpuDxeSmm.h"
  6. //
  7. // attributes for reserved memory before it is promoted to system memory
  8. //
  9. #define EFI_MEMORY_PRESENT 0x0100000000000000ULL
  10. #define EFI_MEMORY_INITIALIZED 0x0200000000000000ULL
  11. #define EFI_MEMORY_TESTED 0x0400000000000000ULL
  12. #define PREVIOUS_MEMORY_DESCRIPTOR(MemoryDescriptor, Size) \
  13. ((EFI_MEMORY_DESCRIPTOR *)((UINT8 *)(MemoryDescriptor) - (Size)))
  14. EFI_MEMORY_DESCRIPTOR *mUefiMemoryMap;
  15. UINTN mUefiMemoryMapSize;
  16. UINTN mUefiDescriptorSize;
  17. EFI_GCD_MEMORY_SPACE_DESCRIPTOR *mGcdMemSpace = NULL;
  18. UINTN mGcdMemNumberOfDesc = 0;
  19. EFI_MEMORY_ATTRIBUTES_TABLE *mUefiMemoryAttributesTable = NULL;
  20. PAGE_ATTRIBUTE_TABLE mPageAttributeTable[] = {
  21. { Page4K, SIZE_4KB, PAGING_4K_ADDRESS_MASK_64 },
  22. { Page2M, SIZE_2MB, PAGING_2M_ADDRESS_MASK_64 },
  23. { Page1G, SIZE_1GB, PAGING_1G_ADDRESS_MASK_64 },
  24. };
  25. BOOLEAN mIsShadowStack = FALSE;
  26. BOOLEAN m5LevelPagingNeeded = FALSE;
  27. //
  28. // Global variable to keep track current available memory used as page table.
  29. //
  30. PAGE_TABLE_POOL *mPageTablePool = NULL;
  31. //
  32. // If memory used by SMM page table has been mareked as ReadOnly.
  33. //
  34. BOOLEAN mIsReadOnlyPageTable = FALSE;
  35. /**
  36. Initialize a buffer pool for page table use only.
  37. To reduce the potential split operation on page table, the pages reserved for
  38. page table should be allocated in the times of PAGE_TABLE_POOL_UNIT_PAGES and
  39. at the boundary of PAGE_TABLE_POOL_ALIGNMENT. So the page pool is always
  40. initialized with number of pages greater than or equal to the given PoolPages.
  41. Once the pages in the pool are used up, this method should be called again to
  42. reserve at least another PAGE_TABLE_POOL_UNIT_PAGES. But usually this won't
  43. happen in practice.
  44. @param PoolPages The least page number of the pool to be created.
  45. @retval TRUE The pool is initialized successfully.
  46. @retval FALSE The memory is out of resource.
  47. **/
  48. BOOLEAN
  49. InitializePageTablePool (
  50. IN UINTN PoolPages
  51. )
  52. {
  53. VOID *Buffer;
  54. BOOLEAN CetEnabled;
  55. BOOLEAN WpEnabled;
  56. IA32_CR0 Cr0;
  57. //
  58. // Always reserve at least PAGE_TABLE_POOL_UNIT_PAGES, including one page for
  59. // header.
  60. //
  61. PoolPages += 1; // Add one page for header.
  62. PoolPages = ((PoolPages - 1) / PAGE_TABLE_POOL_UNIT_PAGES + 1) *
  63. PAGE_TABLE_POOL_UNIT_PAGES;
  64. Buffer = AllocateAlignedPages (PoolPages, PAGE_TABLE_POOL_ALIGNMENT);
  65. if (Buffer == NULL) {
  66. DEBUG ((DEBUG_ERROR, "ERROR: Out of aligned pages\r\n"));
  67. return FALSE;
  68. }
  69. //
  70. // Link all pools into a list for easier track later.
  71. //
  72. if (mPageTablePool == NULL) {
  73. mPageTablePool = Buffer;
  74. mPageTablePool->NextPool = mPageTablePool;
  75. } else {
  76. ((PAGE_TABLE_POOL *)Buffer)->NextPool = mPageTablePool->NextPool;
  77. mPageTablePool->NextPool = Buffer;
  78. mPageTablePool = Buffer;
  79. }
  80. //
  81. // Reserve one page for pool header.
  82. //
  83. mPageTablePool->FreePages = PoolPages - 1;
  84. mPageTablePool->Offset = EFI_PAGES_TO_SIZE (1);
  85. //
  86. // If page table memory has been marked as RO, mark the new pool pages as read-only.
  87. //
  88. if (mIsReadOnlyPageTable) {
  89. CetEnabled = ((AsmReadCr4 () & CR4_CET_ENABLE) != 0) ? TRUE : FALSE;
  90. Cr0.UintN = AsmReadCr0 ();
  91. WpEnabled = (Cr0.Bits.WP != 0) ? TRUE : FALSE;
  92. if (WpEnabled) {
  93. if (CetEnabled) {
  94. //
  95. // CET must be disabled if WP is disabled. Disable CET before clearing CR0.WP.
  96. //
  97. DisableCet ();
  98. }
  99. Cr0.Bits.WP = 0;
  100. AsmWriteCr0 (Cr0.UintN);
  101. }
  102. SmmSetMemoryAttributes ((EFI_PHYSICAL_ADDRESS)(UINTN)Buffer, EFI_PAGES_TO_SIZE (PoolPages), EFI_MEMORY_RO);
  103. if (WpEnabled) {
  104. Cr0.UintN = AsmReadCr0 ();
  105. Cr0.Bits.WP = 1;
  106. AsmWriteCr0 (Cr0.UintN);
  107. if (CetEnabled) {
  108. //
  109. // re-enable CET.
  110. //
  111. EnableCet ();
  112. }
  113. }
  114. }
  115. return TRUE;
  116. }
  117. /**
  118. This API provides a way to allocate memory for page table.
  119. This API can be called more once to allocate memory for page tables.
  120. Allocates the number of 4KB pages of type EfiRuntimeServicesData and returns a pointer to the
  121. allocated buffer. The buffer returned is aligned on a 4KB boundary. If Pages is 0, then NULL
  122. is returned. If there is not enough memory remaining to satisfy the request, then NULL is
  123. returned.
  124. @param Pages The number of 4 KB pages to allocate.
  125. @return A pointer to the allocated buffer or NULL if allocation fails.
  126. **/
  127. VOID *
  128. AllocatePageTableMemory (
  129. IN UINTN Pages
  130. )
  131. {
  132. VOID *Buffer;
  133. if (Pages == 0) {
  134. return NULL;
  135. }
  136. //
  137. // Renew the pool if necessary.
  138. //
  139. if ((mPageTablePool == NULL) ||
  140. (Pages > mPageTablePool->FreePages))
  141. {
  142. if (!InitializePageTablePool (Pages)) {
  143. return NULL;
  144. }
  145. }
  146. Buffer = (UINT8 *)mPageTablePool + mPageTablePool->Offset;
  147. mPageTablePool->Offset += EFI_PAGES_TO_SIZE (Pages);
  148. mPageTablePool->FreePages -= Pages;
  149. return Buffer;
  150. }
  151. /**
  152. Return length according to page attributes.
  153. @param[in] PageAttributes The page attribute of the page entry.
  154. @return The length of page entry.
  155. **/
  156. UINTN
  157. PageAttributeToLength (
  158. IN PAGE_ATTRIBUTE PageAttribute
  159. )
  160. {
  161. UINTN Index;
  162. for (Index = 0; Index < sizeof (mPageAttributeTable)/sizeof (mPageAttributeTable[0]); Index++) {
  163. if (PageAttribute == mPageAttributeTable[Index].Attribute) {
  164. return (UINTN)mPageAttributeTable[Index].Length;
  165. }
  166. }
  167. return 0;
  168. }
  169. /**
  170. Return address mask according to page attributes.
  171. @param[in] PageAttributes The page attribute of the page entry.
  172. @return The address mask of page entry.
  173. **/
  174. UINTN
  175. PageAttributeToMask (
  176. IN PAGE_ATTRIBUTE PageAttribute
  177. )
  178. {
  179. UINTN Index;
  180. for (Index = 0; Index < sizeof (mPageAttributeTable)/sizeof (mPageAttributeTable[0]); Index++) {
  181. if (PageAttribute == mPageAttributeTable[Index].Attribute) {
  182. return (UINTN)mPageAttributeTable[Index].AddressMask;
  183. }
  184. }
  185. return 0;
  186. }
  187. /**
  188. Return page table entry to match the address.
  189. @param[in] PageTableBase The page table base.
  190. @param[in] Enable5LevelPaging If PML5 paging is enabled.
  191. @param[in] Address The address to be checked.
  192. @param[out] PageAttributes The page attribute of the page entry.
  193. @return The page entry.
  194. **/
  195. VOID *
  196. GetPageTableEntry (
  197. IN UINTN PageTableBase,
  198. IN BOOLEAN Enable5LevelPaging,
  199. IN PHYSICAL_ADDRESS Address,
  200. OUT PAGE_ATTRIBUTE *PageAttribute
  201. )
  202. {
  203. UINTN Index1;
  204. UINTN Index2;
  205. UINTN Index3;
  206. UINTN Index4;
  207. UINTN Index5;
  208. UINT64 *L1PageTable;
  209. UINT64 *L2PageTable;
  210. UINT64 *L3PageTable;
  211. UINT64 *L4PageTable;
  212. UINT64 *L5PageTable;
  213. Index5 = ((UINTN)RShiftU64 (Address, 48)) & PAGING_PAE_INDEX_MASK;
  214. Index4 = ((UINTN)RShiftU64 (Address, 39)) & PAGING_PAE_INDEX_MASK;
  215. Index3 = ((UINTN)Address >> 30) & PAGING_PAE_INDEX_MASK;
  216. Index2 = ((UINTN)Address >> 21) & PAGING_PAE_INDEX_MASK;
  217. Index1 = ((UINTN)Address >> 12) & PAGING_PAE_INDEX_MASK;
  218. if (sizeof (UINTN) == sizeof (UINT64)) {
  219. if (Enable5LevelPaging) {
  220. L5PageTable = (UINT64 *)PageTableBase;
  221. if (L5PageTable[Index5] == 0) {
  222. *PageAttribute = PageNone;
  223. return NULL;
  224. }
  225. L4PageTable = (UINT64 *)(UINTN)(L5PageTable[Index5] & ~mAddressEncMask & PAGING_4K_ADDRESS_MASK_64);
  226. } else {
  227. L4PageTable = (UINT64 *)PageTableBase;
  228. }
  229. if (L4PageTable[Index4] == 0) {
  230. *PageAttribute = PageNone;
  231. return NULL;
  232. }
  233. L3PageTable = (UINT64 *)(UINTN)(L4PageTable[Index4] & ~mAddressEncMask & PAGING_4K_ADDRESS_MASK_64);
  234. } else {
  235. L3PageTable = (UINT64 *)PageTableBase;
  236. }
  237. if (L3PageTable[Index3] == 0) {
  238. *PageAttribute = PageNone;
  239. return NULL;
  240. }
  241. if ((L3PageTable[Index3] & IA32_PG_PS) != 0) {
  242. // 1G
  243. *PageAttribute = Page1G;
  244. return &L3PageTable[Index3];
  245. }
  246. L2PageTable = (UINT64 *)(UINTN)(L3PageTable[Index3] & ~mAddressEncMask & PAGING_4K_ADDRESS_MASK_64);
  247. if (L2PageTable[Index2] == 0) {
  248. *PageAttribute = PageNone;
  249. return NULL;
  250. }
  251. if ((L2PageTable[Index2] & IA32_PG_PS) != 0) {
  252. // 2M
  253. *PageAttribute = Page2M;
  254. return &L2PageTable[Index2];
  255. }
  256. // 4k
  257. L1PageTable = (UINT64 *)(UINTN)(L2PageTable[Index2] & ~mAddressEncMask & PAGING_4K_ADDRESS_MASK_64);
  258. if ((L1PageTable[Index1] == 0) && (Address != 0)) {
  259. *PageAttribute = PageNone;
  260. return NULL;
  261. }
  262. *PageAttribute = Page4K;
  263. return &L1PageTable[Index1];
  264. }
  265. /**
  266. Return memory attributes of page entry.
  267. @param[in] PageEntry The page entry.
  268. @return Memory attributes of page entry.
  269. **/
  270. UINT64
  271. GetAttributesFromPageEntry (
  272. IN UINT64 *PageEntry
  273. )
  274. {
  275. UINT64 Attributes;
  276. Attributes = 0;
  277. if ((*PageEntry & IA32_PG_P) == 0) {
  278. Attributes |= EFI_MEMORY_RP;
  279. }
  280. if ((*PageEntry & IA32_PG_RW) == 0) {
  281. Attributes |= EFI_MEMORY_RO;
  282. }
  283. if ((*PageEntry & IA32_PG_NX) != 0) {
  284. Attributes |= EFI_MEMORY_XP;
  285. }
  286. return Attributes;
  287. }
  288. /**
  289. Modify memory attributes of page entry.
  290. @param[in] PageEntry The page entry.
  291. @param[in] Attributes The bit mask of attributes to modify for the memory region.
  292. @param[in] IsSet TRUE means to set attributes. FALSE means to clear attributes.
  293. @param[out] IsModified TRUE means page table modified. FALSE means page table not modified.
  294. **/
  295. VOID
  296. ConvertPageEntryAttribute (
  297. IN UINT64 *PageEntry,
  298. IN UINT64 Attributes,
  299. IN BOOLEAN IsSet,
  300. OUT BOOLEAN *IsModified
  301. )
  302. {
  303. UINT64 CurrentPageEntry;
  304. UINT64 NewPageEntry;
  305. CurrentPageEntry = *PageEntry;
  306. NewPageEntry = CurrentPageEntry;
  307. if ((Attributes & EFI_MEMORY_RP) != 0) {
  308. if (IsSet) {
  309. NewPageEntry &= ~(UINT64)IA32_PG_P;
  310. } else {
  311. NewPageEntry |= IA32_PG_P;
  312. }
  313. }
  314. if ((Attributes & EFI_MEMORY_RO) != 0) {
  315. if (IsSet) {
  316. NewPageEntry &= ~(UINT64)IA32_PG_RW;
  317. if (mIsShadowStack) {
  318. // Environment setup
  319. // ReadOnly page need set Dirty bit for shadow stack
  320. NewPageEntry |= IA32_PG_D;
  321. // Clear user bit for supervisor shadow stack
  322. NewPageEntry &= ~(UINT64)IA32_PG_U;
  323. } else {
  324. // Runtime update
  325. // Clear dirty bit for non shadow stack, to protect RO page.
  326. NewPageEntry &= ~(UINT64)IA32_PG_D;
  327. }
  328. } else {
  329. NewPageEntry |= IA32_PG_RW;
  330. }
  331. }
  332. if ((Attributes & EFI_MEMORY_XP) != 0) {
  333. if (mXdSupported) {
  334. if (IsSet) {
  335. NewPageEntry |= IA32_PG_NX;
  336. } else {
  337. NewPageEntry &= ~IA32_PG_NX;
  338. }
  339. }
  340. }
  341. *PageEntry = NewPageEntry;
  342. if (CurrentPageEntry != NewPageEntry) {
  343. *IsModified = TRUE;
  344. DEBUG ((DEBUG_VERBOSE, "ConvertPageEntryAttribute 0x%lx", CurrentPageEntry));
  345. DEBUG ((DEBUG_VERBOSE, "->0x%lx\n", NewPageEntry));
  346. } else {
  347. *IsModified = FALSE;
  348. }
  349. }
  350. /**
  351. This function returns if there is need to split page entry.
  352. @param[in] BaseAddress The base address to be checked.
  353. @param[in] Length The length to be checked.
  354. @param[in] PageEntry The page entry to be checked.
  355. @param[in] PageAttribute The page attribute of the page entry.
  356. @retval SplitAttributes on if there is need to split page entry.
  357. **/
  358. PAGE_ATTRIBUTE
  359. NeedSplitPage (
  360. IN PHYSICAL_ADDRESS BaseAddress,
  361. IN UINT64 Length,
  362. IN UINT64 *PageEntry,
  363. IN PAGE_ATTRIBUTE PageAttribute
  364. )
  365. {
  366. UINT64 PageEntryLength;
  367. PageEntryLength = PageAttributeToLength (PageAttribute);
  368. if (((BaseAddress & (PageEntryLength - 1)) == 0) && (Length >= PageEntryLength)) {
  369. return PageNone;
  370. }
  371. if (((BaseAddress & PAGING_2M_MASK) != 0) || (Length < SIZE_2MB)) {
  372. return Page4K;
  373. }
  374. return Page2M;
  375. }
  376. /**
  377. This function splits one page entry to small page entries.
  378. @param[in] PageEntry The page entry to be splitted.
  379. @param[in] PageAttribute The page attribute of the page entry.
  380. @param[in] SplitAttribute How to split the page entry.
  381. @retval RETURN_SUCCESS The page entry is splitted.
  382. @retval RETURN_UNSUPPORTED The page entry does not support to be splitted.
  383. @retval RETURN_OUT_OF_RESOURCES No resource to split page entry.
  384. **/
  385. RETURN_STATUS
  386. SplitPage (
  387. IN UINT64 *PageEntry,
  388. IN PAGE_ATTRIBUTE PageAttribute,
  389. IN PAGE_ATTRIBUTE SplitAttribute
  390. )
  391. {
  392. UINT64 BaseAddress;
  393. UINT64 *NewPageEntry;
  394. UINTN Index;
  395. ASSERT (PageAttribute == Page2M || PageAttribute == Page1G);
  396. if (PageAttribute == Page2M) {
  397. //
  398. // Split 2M to 4K
  399. //
  400. ASSERT (SplitAttribute == Page4K);
  401. if (SplitAttribute == Page4K) {
  402. NewPageEntry = AllocatePageTableMemory (1);
  403. DEBUG ((DEBUG_VERBOSE, "Split - 0x%x\n", NewPageEntry));
  404. if (NewPageEntry == NULL) {
  405. return RETURN_OUT_OF_RESOURCES;
  406. }
  407. BaseAddress = *PageEntry & PAGING_2M_ADDRESS_MASK_64;
  408. for (Index = 0; Index < SIZE_4KB / sizeof (UINT64); Index++) {
  409. NewPageEntry[Index] = (BaseAddress + SIZE_4KB * Index) | mAddressEncMask | ((*PageEntry) & PAGE_PROGATE_BITS);
  410. }
  411. (*PageEntry) = (UINT64)(UINTN)NewPageEntry | mAddressEncMask | PAGE_ATTRIBUTE_BITS;
  412. return RETURN_SUCCESS;
  413. } else {
  414. return RETURN_UNSUPPORTED;
  415. }
  416. } else if (PageAttribute == Page1G) {
  417. //
  418. // Split 1G to 2M
  419. // No need support 1G->4K directly, we should use 1G->2M, then 2M->4K to get more compact page table.
  420. //
  421. ASSERT (SplitAttribute == Page2M || SplitAttribute == Page4K);
  422. if (((SplitAttribute == Page2M) || (SplitAttribute == Page4K))) {
  423. NewPageEntry = AllocatePageTableMemory (1);
  424. DEBUG ((DEBUG_VERBOSE, "Split - 0x%x\n", NewPageEntry));
  425. if (NewPageEntry == NULL) {
  426. return RETURN_OUT_OF_RESOURCES;
  427. }
  428. BaseAddress = *PageEntry & PAGING_1G_ADDRESS_MASK_64;
  429. for (Index = 0; Index < SIZE_4KB / sizeof (UINT64); Index++) {
  430. NewPageEntry[Index] = (BaseAddress + SIZE_2MB * Index) | mAddressEncMask | IA32_PG_PS | ((*PageEntry) & PAGE_PROGATE_BITS);
  431. }
  432. (*PageEntry) = (UINT64)(UINTN)NewPageEntry | mAddressEncMask | PAGE_ATTRIBUTE_BITS;
  433. return RETURN_SUCCESS;
  434. } else {
  435. return RETURN_UNSUPPORTED;
  436. }
  437. } else {
  438. return RETURN_UNSUPPORTED;
  439. }
  440. }
  441. /**
  442. This function modifies the page attributes for the memory region specified by BaseAddress and
  443. Length from their current attributes to the attributes specified by Attributes.
  444. Caller should make sure BaseAddress and Length is at page boundary.
  445. @param[in] PageTableBase The page table base.
  446. @param[in] EnablePML5Paging If PML5 paging is enabled.
  447. @param[in] BaseAddress The physical address that is the start address of a memory region.
  448. @param[in] Length The size in bytes of the memory region.
  449. @param[in] Attributes The bit mask of attributes to modify for the memory region.
  450. @param[in] IsSet TRUE means to set attributes. FALSE means to clear attributes.
  451. @param[out] IsSplitted TRUE means page table splitted. FALSE means page table not splitted.
  452. @param[out] IsModified TRUE means page table modified. FALSE means page table not modified.
  453. @retval RETURN_SUCCESS The attributes were modified for the memory region.
  454. @retval RETURN_ACCESS_DENIED The attributes for the memory resource range specified by
  455. BaseAddress and Length cannot be modified.
  456. @retval RETURN_INVALID_PARAMETER Length is zero.
  457. Attributes specified an illegal combination of attributes that
  458. cannot be set together.
  459. @retval RETURN_OUT_OF_RESOURCES There are not enough system resources to modify the attributes of
  460. the memory resource range.
  461. @retval RETURN_UNSUPPORTED The processor does not support one or more bytes of the memory
  462. resource range specified by BaseAddress and Length.
  463. The bit mask of attributes is not support for the memory resource
  464. range specified by BaseAddress and Length.
  465. **/
  466. RETURN_STATUS
  467. ConvertMemoryPageAttributes (
  468. IN UINTN PageTableBase,
  469. IN BOOLEAN EnablePML5Paging,
  470. IN PHYSICAL_ADDRESS BaseAddress,
  471. IN UINT64 Length,
  472. IN UINT64 Attributes,
  473. IN BOOLEAN IsSet,
  474. OUT BOOLEAN *IsSplitted OPTIONAL,
  475. OUT BOOLEAN *IsModified OPTIONAL
  476. )
  477. {
  478. UINT64 *PageEntry;
  479. PAGE_ATTRIBUTE PageAttribute;
  480. UINTN PageEntryLength;
  481. PAGE_ATTRIBUTE SplitAttribute;
  482. RETURN_STATUS Status;
  483. BOOLEAN IsEntryModified;
  484. EFI_PHYSICAL_ADDRESS MaximumSupportMemAddress;
  485. ASSERT (Attributes != 0);
  486. ASSERT ((Attributes & ~EFI_MEMORY_ATTRIBUTE_MASK) == 0);
  487. ASSERT ((BaseAddress & (SIZE_4KB - 1)) == 0);
  488. ASSERT ((Length & (SIZE_4KB - 1)) == 0);
  489. if (Length == 0) {
  490. return RETURN_INVALID_PARAMETER;
  491. }
  492. MaximumSupportMemAddress = (EFI_PHYSICAL_ADDRESS)(UINTN)(LShiftU64 (1, mPhysicalAddressBits) - 1);
  493. if (BaseAddress > MaximumSupportMemAddress) {
  494. return RETURN_UNSUPPORTED;
  495. }
  496. if (Length > MaximumSupportMemAddress) {
  497. return RETURN_UNSUPPORTED;
  498. }
  499. if ((Length != 0) && (BaseAddress > MaximumSupportMemAddress - (Length - 1))) {
  500. return RETURN_UNSUPPORTED;
  501. }
  502. // DEBUG ((DEBUG_ERROR, "ConvertMemoryPageAttributes(%x) - %016lx, %016lx, %02lx\n", IsSet, BaseAddress, Length, Attributes));
  503. if (IsSplitted != NULL) {
  504. *IsSplitted = FALSE;
  505. }
  506. if (IsModified != NULL) {
  507. *IsModified = FALSE;
  508. }
  509. //
  510. // Below logic is to check 2M/4K page to make sure we do not waste memory.
  511. //
  512. while (Length != 0) {
  513. PageEntry = GetPageTableEntry (PageTableBase, EnablePML5Paging, BaseAddress, &PageAttribute);
  514. if (PageEntry == NULL) {
  515. return RETURN_UNSUPPORTED;
  516. }
  517. PageEntryLength = PageAttributeToLength (PageAttribute);
  518. SplitAttribute = NeedSplitPage (BaseAddress, Length, PageEntry, PageAttribute);
  519. if (SplitAttribute == PageNone) {
  520. ConvertPageEntryAttribute (PageEntry, Attributes, IsSet, &IsEntryModified);
  521. if (IsEntryModified) {
  522. if (IsModified != NULL) {
  523. *IsModified = TRUE;
  524. }
  525. }
  526. //
  527. // Convert success, move to next
  528. //
  529. BaseAddress += PageEntryLength;
  530. Length -= PageEntryLength;
  531. } else {
  532. Status = SplitPage (PageEntry, PageAttribute, SplitAttribute);
  533. if (RETURN_ERROR (Status)) {
  534. return RETURN_UNSUPPORTED;
  535. }
  536. if (IsSplitted != NULL) {
  537. *IsSplitted = TRUE;
  538. }
  539. if (IsModified != NULL) {
  540. *IsModified = TRUE;
  541. }
  542. //
  543. // Just split current page
  544. // Convert success in next around
  545. //
  546. }
  547. }
  548. return RETURN_SUCCESS;
  549. }
  550. /**
  551. FlushTlb on current processor.
  552. @param[in,out] Buffer Pointer to private data buffer.
  553. **/
  554. VOID
  555. EFIAPI
  556. FlushTlbOnCurrentProcessor (
  557. IN OUT VOID *Buffer
  558. )
  559. {
  560. CpuFlushTlb ();
  561. }
  562. /**
  563. FlushTlb for all processors.
  564. **/
  565. VOID
  566. FlushTlbForAll (
  567. VOID
  568. )
  569. {
  570. UINTN Index;
  571. FlushTlbOnCurrentProcessor (NULL);
  572. for (Index = 0; Index < gSmst->NumberOfCpus; Index++) {
  573. if (Index != gSmst->CurrentlyExecutingCpu) {
  574. // Force to start up AP in blocking mode,
  575. SmmBlockingStartupThisAp (FlushTlbOnCurrentProcessor, Index, NULL);
  576. // Do not check return status, because AP might not be present in some corner cases.
  577. }
  578. }
  579. }
  580. /**
  581. This function sets the attributes for the memory region specified by BaseAddress and
  582. Length from their current attributes to the attributes specified by Attributes.
  583. @param[in] PageTableBase The page table base.
  584. @param[in] EnablePML5Paging If PML5 paging is enabled.
  585. @param[in] BaseAddress The physical address that is the start address of a memory region.
  586. @param[in] Length The size in bytes of the memory region.
  587. @param[in] Attributes The bit mask of attributes to set for the memory region.
  588. @param[out] IsSplitted TRUE means page table splitted. FALSE means page table not splitted.
  589. @retval EFI_SUCCESS The attributes were set for the memory region.
  590. @retval EFI_ACCESS_DENIED The attributes for the memory resource range specified by
  591. BaseAddress and Length cannot be modified.
  592. @retval EFI_INVALID_PARAMETER Length is zero.
  593. Attributes specified an illegal combination of attributes that
  594. cannot be set together.
  595. @retval EFI_OUT_OF_RESOURCES There are not enough system resources to modify the attributes of
  596. the memory resource range.
  597. @retval EFI_UNSUPPORTED The processor does not support one or more bytes of the memory
  598. resource range specified by BaseAddress and Length.
  599. The bit mask of attributes is not support for the memory resource
  600. range specified by BaseAddress and Length.
  601. **/
  602. EFI_STATUS
  603. SmmSetMemoryAttributesEx (
  604. IN UINTN PageTableBase,
  605. IN BOOLEAN EnablePML5Paging,
  606. IN EFI_PHYSICAL_ADDRESS BaseAddress,
  607. IN UINT64 Length,
  608. IN UINT64 Attributes,
  609. OUT BOOLEAN *IsSplitted OPTIONAL
  610. )
  611. {
  612. EFI_STATUS Status;
  613. BOOLEAN IsModified;
  614. Status = ConvertMemoryPageAttributes (PageTableBase, EnablePML5Paging, BaseAddress, Length, Attributes, TRUE, IsSplitted, &IsModified);
  615. if (!EFI_ERROR (Status)) {
  616. if (IsModified) {
  617. //
  618. // Flush TLB as last step
  619. //
  620. FlushTlbForAll ();
  621. }
  622. }
  623. return Status;
  624. }
  625. /**
  626. This function clears the attributes for the memory region specified by BaseAddress and
  627. Length from their current attributes to the attributes specified by Attributes.
  628. @param[in] PageTableBase The page table base.
  629. @param[in] EnablePML5Paging If PML5 paging is enabled.
  630. @param[in] BaseAddress The physical address that is the start address of a memory region.
  631. @param[in] Length The size in bytes of the memory region.
  632. @param[in] Attributes The bit mask of attributes to clear for the memory region.
  633. @param[out] IsSplitted TRUE means page table splitted. FALSE means page table not splitted.
  634. @retval EFI_SUCCESS The attributes were cleared for the memory region.
  635. @retval EFI_ACCESS_DENIED The attributes for the memory resource range specified by
  636. BaseAddress and Length cannot be modified.
  637. @retval EFI_INVALID_PARAMETER Length is zero.
  638. Attributes specified an illegal combination of attributes that
  639. cannot be cleared together.
  640. @retval EFI_OUT_OF_RESOURCES There are not enough system resources to modify the attributes of
  641. the memory resource range.
  642. @retval EFI_UNSUPPORTED The processor does not support one or more bytes of the memory
  643. resource range specified by BaseAddress and Length.
  644. The bit mask of attributes is not supported for the memory resource
  645. range specified by BaseAddress and Length.
  646. **/
  647. EFI_STATUS
  648. SmmClearMemoryAttributesEx (
  649. IN UINTN PageTableBase,
  650. IN BOOLEAN EnablePML5Paging,
  651. IN EFI_PHYSICAL_ADDRESS BaseAddress,
  652. IN UINT64 Length,
  653. IN UINT64 Attributes,
  654. OUT BOOLEAN *IsSplitted OPTIONAL
  655. )
  656. {
  657. EFI_STATUS Status;
  658. BOOLEAN IsModified;
  659. Status = ConvertMemoryPageAttributes (PageTableBase, EnablePML5Paging, BaseAddress, Length, Attributes, FALSE, IsSplitted, &IsModified);
  660. if (!EFI_ERROR (Status)) {
  661. if (IsModified) {
  662. //
  663. // Flush TLB as last step
  664. //
  665. FlushTlbForAll ();
  666. }
  667. }
  668. return Status;
  669. }
  670. /**
  671. This function sets the attributes for the memory region specified by BaseAddress and
  672. Length from their current attributes to the attributes specified by Attributes.
  673. @param[in] BaseAddress The physical address that is the start address of a memory region.
  674. @param[in] Length The size in bytes of the memory region.
  675. @param[in] Attributes The bit mask of attributes to set for the memory region.
  676. @retval EFI_SUCCESS The attributes were set for the memory region.
  677. @retval EFI_ACCESS_DENIED The attributes for the memory resource range specified by
  678. BaseAddress and Length cannot be modified.
  679. @retval EFI_INVALID_PARAMETER Length is zero.
  680. Attributes specified an illegal combination of attributes that
  681. cannot be set together.
  682. @retval EFI_OUT_OF_RESOURCES There are not enough system resources to modify the attributes of
  683. the memory resource range.
  684. @retval EFI_UNSUPPORTED The processor does not support one or more bytes of the memory
  685. resource range specified by BaseAddress and Length.
  686. The bit mask of attributes is not supported for the memory resource
  687. range specified by BaseAddress and Length.
  688. **/
  689. EFI_STATUS
  690. SmmSetMemoryAttributes (
  691. IN EFI_PHYSICAL_ADDRESS BaseAddress,
  692. IN UINT64 Length,
  693. IN UINT64 Attributes
  694. )
  695. {
  696. IA32_CR4 Cr4;
  697. UINTN PageTableBase;
  698. BOOLEAN Enable5LevelPaging;
  699. PageTableBase = AsmReadCr3 () & PAGING_4K_ADDRESS_MASK_64;
  700. Cr4.UintN = AsmReadCr4 ();
  701. Enable5LevelPaging = (BOOLEAN)(Cr4.Bits.LA57 == 1);
  702. return SmmSetMemoryAttributesEx (PageTableBase, Enable5LevelPaging, BaseAddress, Length, Attributes, NULL);
  703. }
  704. /**
  705. This function clears the attributes for the memory region specified by BaseAddress and
  706. Length from their current attributes to the attributes specified by Attributes.
  707. @param[in] BaseAddress The physical address that is the start address of a memory region.
  708. @param[in] Length The size in bytes of the memory region.
  709. @param[in] Attributes The bit mask of attributes to clear for the memory region.
  710. @retval EFI_SUCCESS The attributes were cleared for the memory region.
  711. @retval EFI_ACCESS_DENIED The attributes for the memory resource range specified by
  712. BaseAddress and Length cannot be modified.
  713. @retval EFI_INVALID_PARAMETER Length is zero.
  714. Attributes specified an illegal combination of attributes that
  715. cannot be cleared together.
  716. @retval EFI_OUT_OF_RESOURCES There are not enough system resources to modify the attributes of
  717. the memory resource range.
  718. @retval EFI_UNSUPPORTED The processor does not support one or more bytes of the memory
  719. resource range specified by BaseAddress and Length.
  720. The bit mask of attributes is not supported for the memory resource
  721. range specified by BaseAddress and Length.
  722. **/
  723. EFI_STATUS
  724. SmmClearMemoryAttributes (
  725. IN EFI_PHYSICAL_ADDRESS BaseAddress,
  726. IN UINT64 Length,
  727. IN UINT64 Attributes
  728. )
  729. {
  730. IA32_CR4 Cr4;
  731. UINTN PageTableBase;
  732. BOOLEAN Enable5LevelPaging;
  733. PageTableBase = AsmReadCr3 () & PAGING_4K_ADDRESS_MASK_64;
  734. Cr4.UintN = AsmReadCr4 ();
  735. Enable5LevelPaging = (BOOLEAN)(Cr4.Bits.LA57 == 1);
  736. return SmmClearMemoryAttributesEx (PageTableBase, Enable5LevelPaging, BaseAddress, Length, Attributes, NULL);
  737. }
  738. /**
  739. Set ShadowStack memory.
  740. @param[in] Cr3 The page table base address.
  741. @param[in] BaseAddress The physical address that is the start address of a memory region.
  742. @param[in] Length The size in bytes of the memory region.
  743. @retval EFI_SUCCESS The shadow stack memory is set.
  744. **/
  745. EFI_STATUS
  746. SetShadowStack (
  747. IN UINTN Cr3,
  748. IN EFI_PHYSICAL_ADDRESS BaseAddress,
  749. IN UINT64 Length
  750. )
  751. {
  752. EFI_STATUS Status;
  753. mIsShadowStack = TRUE;
  754. Status = SmmSetMemoryAttributesEx (Cr3, m5LevelPagingNeeded, BaseAddress, Length, EFI_MEMORY_RO, NULL);
  755. mIsShadowStack = FALSE;
  756. return Status;
  757. }
  758. /**
  759. Set not present memory.
  760. @param[in] Cr3 The page table base address.
  761. @param[in] BaseAddress The physical address that is the start address of a memory region.
  762. @param[in] Length The size in bytes of the memory region.
  763. @retval EFI_SUCCESS The not present memory is set.
  764. **/
  765. EFI_STATUS
  766. SetNotPresentPage (
  767. IN UINTN Cr3,
  768. IN EFI_PHYSICAL_ADDRESS BaseAddress,
  769. IN UINT64 Length
  770. )
  771. {
  772. EFI_STATUS Status;
  773. Status = SmmSetMemoryAttributesEx (Cr3, m5LevelPagingNeeded, BaseAddress, Length, EFI_MEMORY_RP, NULL);
  774. return Status;
  775. }
  776. /**
  777. Retrieves a pointer to the system configuration table from the SMM System Table
  778. based on a specified GUID.
  779. @param[in] TableGuid The pointer to table's GUID type.
  780. @param[out] Table The pointer to the table associated with TableGuid in the EFI System Table.
  781. @retval EFI_SUCCESS A configuration table matching TableGuid was found.
  782. @retval EFI_NOT_FOUND A configuration table matching TableGuid could not be found.
  783. **/
  784. EFI_STATUS
  785. EFIAPI
  786. SmmGetSystemConfigurationTable (
  787. IN EFI_GUID *TableGuid,
  788. OUT VOID **Table
  789. )
  790. {
  791. UINTN Index;
  792. ASSERT (TableGuid != NULL);
  793. ASSERT (Table != NULL);
  794. *Table = NULL;
  795. for (Index = 0; Index < gSmst->NumberOfTableEntries; Index++) {
  796. if (CompareGuid (TableGuid, &(gSmst->SmmConfigurationTable[Index].VendorGuid))) {
  797. *Table = gSmst->SmmConfigurationTable[Index].VendorTable;
  798. return EFI_SUCCESS;
  799. }
  800. }
  801. return EFI_NOT_FOUND;
  802. }
  803. /**
  804. This function sets SMM save state buffer to be RW and XP.
  805. **/
  806. VOID
  807. PatchSmmSaveStateMap (
  808. VOID
  809. )
  810. {
  811. UINTN Index;
  812. UINTN TileCodeSize;
  813. UINTN TileDataSize;
  814. UINTN TileSize;
  815. TileCodeSize = GetSmiHandlerSize ();
  816. TileCodeSize = ALIGN_VALUE (TileCodeSize, SIZE_4KB);
  817. TileDataSize = (SMRAM_SAVE_STATE_MAP_OFFSET - SMM_PSD_OFFSET) + sizeof (SMRAM_SAVE_STATE_MAP);
  818. TileDataSize = ALIGN_VALUE (TileDataSize, SIZE_4KB);
  819. TileSize = TileDataSize + TileCodeSize - 1;
  820. TileSize = 2 * GetPowerOfTwo32 ((UINT32)TileSize);
  821. DEBUG ((DEBUG_INFO, "PatchSmmSaveStateMap:\n"));
  822. for (Index = 0; Index < mMaxNumberOfCpus - 1; Index++) {
  823. //
  824. // Code
  825. //
  826. SmmSetMemoryAttributes (
  827. mCpuHotPlugData.SmBase[Index] + SMM_HANDLER_OFFSET,
  828. TileCodeSize,
  829. EFI_MEMORY_RO
  830. );
  831. SmmClearMemoryAttributes (
  832. mCpuHotPlugData.SmBase[Index] + SMM_HANDLER_OFFSET,
  833. TileCodeSize,
  834. EFI_MEMORY_XP
  835. );
  836. //
  837. // Data
  838. //
  839. SmmClearMemoryAttributes (
  840. mCpuHotPlugData.SmBase[Index] + SMM_HANDLER_OFFSET + TileCodeSize,
  841. TileSize - TileCodeSize,
  842. EFI_MEMORY_RO
  843. );
  844. SmmSetMemoryAttributes (
  845. mCpuHotPlugData.SmBase[Index] + SMM_HANDLER_OFFSET + TileCodeSize,
  846. TileSize - TileCodeSize,
  847. EFI_MEMORY_XP
  848. );
  849. }
  850. //
  851. // Code
  852. //
  853. SmmSetMemoryAttributes (
  854. mCpuHotPlugData.SmBase[mMaxNumberOfCpus - 1] + SMM_HANDLER_OFFSET,
  855. TileCodeSize,
  856. EFI_MEMORY_RO
  857. );
  858. SmmClearMemoryAttributes (
  859. mCpuHotPlugData.SmBase[mMaxNumberOfCpus - 1] + SMM_HANDLER_OFFSET,
  860. TileCodeSize,
  861. EFI_MEMORY_XP
  862. );
  863. //
  864. // Data
  865. //
  866. SmmClearMemoryAttributes (
  867. mCpuHotPlugData.SmBase[mMaxNumberOfCpus - 1] + SMM_HANDLER_OFFSET + TileCodeSize,
  868. SIZE_32KB - TileCodeSize,
  869. EFI_MEMORY_RO
  870. );
  871. SmmSetMemoryAttributes (
  872. mCpuHotPlugData.SmBase[mMaxNumberOfCpus - 1] + SMM_HANDLER_OFFSET + TileCodeSize,
  873. SIZE_32KB - TileCodeSize,
  874. EFI_MEMORY_XP
  875. );
  876. }
  877. /**
  878. This function sets GDT/IDT buffer to be RO and XP.
  879. **/
  880. VOID
  881. PatchGdtIdtMap (
  882. VOID
  883. )
  884. {
  885. EFI_PHYSICAL_ADDRESS BaseAddress;
  886. UINTN Size;
  887. //
  888. // GDT
  889. //
  890. DEBUG ((DEBUG_INFO, "PatchGdtIdtMap - GDT:\n"));
  891. BaseAddress = mGdtBuffer;
  892. Size = ALIGN_VALUE (mGdtBufferSize, SIZE_4KB);
  893. //
  894. // The range should have been set to RO
  895. // if it is allocated with EfiRuntimeServicesCode.
  896. //
  897. SmmSetMemoryAttributes (
  898. BaseAddress,
  899. Size,
  900. EFI_MEMORY_XP
  901. );
  902. //
  903. // IDT
  904. //
  905. DEBUG ((DEBUG_INFO, "PatchGdtIdtMap - IDT:\n"));
  906. BaseAddress = gcSmiIdtr.Base;
  907. Size = ALIGN_VALUE (gcSmiIdtr.Limit + 1, SIZE_4KB);
  908. //
  909. // The range should have been set to RO
  910. // if it is allocated with EfiRuntimeServicesCode.
  911. //
  912. SmmSetMemoryAttributes (
  913. BaseAddress,
  914. Size,
  915. EFI_MEMORY_XP
  916. );
  917. }
  918. /**
  919. This function sets memory attribute according to MemoryAttributesTable.
  920. **/
  921. VOID
  922. SetMemMapAttributes (
  923. VOID
  924. )
  925. {
  926. EFI_MEMORY_DESCRIPTOR *MemoryMap;
  927. EFI_MEMORY_DESCRIPTOR *MemoryMapStart;
  928. UINTN MemoryMapEntryCount;
  929. UINTN DescriptorSize;
  930. UINTN Index;
  931. EDKII_PI_SMM_MEMORY_ATTRIBUTES_TABLE *MemoryAttributesTable;
  932. SmmGetSystemConfigurationTable (&gEdkiiPiSmmMemoryAttributesTableGuid, (VOID **)&MemoryAttributesTable);
  933. if (MemoryAttributesTable == NULL) {
  934. DEBUG ((DEBUG_INFO, "MemoryAttributesTable - NULL\n"));
  935. return;
  936. }
  937. DEBUG ((DEBUG_INFO, "MemoryAttributesTable:\n"));
  938. DEBUG ((DEBUG_INFO, " Version - 0x%08x\n", MemoryAttributesTable->Version));
  939. DEBUG ((DEBUG_INFO, " NumberOfEntries - 0x%08x\n", MemoryAttributesTable->NumberOfEntries));
  940. DEBUG ((DEBUG_INFO, " DescriptorSize - 0x%08x\n", MemoryAttributesTable->DescriptorSize));
  941. MemoryMapEntryCount = MemoryAttributesTable->NumberOfEntries;
  942. DescriptorSize = MemoryAttributesTable->DescriptorSize;
  943. MemoryMapStart = (EFI_MEMORY_DESCRIPTOR *)(MemoryAttributesTable + 1);
  944. MemoryMap = MemoryMapStart;
  945. for (Index = 0; Index < MemoryMapEntryCount; Index++) {
  946. DEBUG ((DEBUG_INFO, "Entry (0x%x)\n", MemoryMap));
  947. DEBUG ((DEBUG_INFO, " Type - 0x%x\n", MemoryMap->Type));
  948. DEBUG ((DEBUG_INFO, " PhysicalStart - 0x%016lx\n", MemoryMap->PhysicalStart));
  949. DEBUG ((DEBUG_INFO, " VirtualStart - 0x%016lx\n", MemoryMap->VirtualStart));
  950. DEBUG ((DEBUG_INFO, " NumberOfPages - 0x%016lx\n", MemoryMap->NumberOfPages));
  951. DEBUG ((DEBUG_INFO, " Attribute - 0x%016lx\n", MemoryMap->Attribute));
  952. MemoryMap = NEXT_MEMORY_DESCRIPTOR (MemoryMap, DescriptorSize);
  953. }
  954. MemoryMap = MemoryMapStart;
  955. for (Index = 0; Index < MemoryMapEntryCount; Index++) {
  956. DEBUG ((DEBUG_VERBOSE, "SetAttribute: Memory Entry - 0x%lx, 0x%x\n", MemoryMap->PhysicalStart, MemoryMap->NumberOfPages));
  957. switch (MemoryMap->Type) {
  958. case EfiRuntimeServicesCode:
  959. SmmSetMemoryAttributes (
  960. MemoryMap->PhysicalStart,
  961. EFI_PAGES_TO_SIZE ((UINTN)MemoryMap->NumberOfPages),
  962. EFI_MEMORY_RO
  963. );
  964. break;
  965. case EfiRuntimeServicesData:
  966. SmmSetMemoryAttributes (
  967. MemoryMap->PhysicalStart,
  968. EFI_PAGES_TO_SIZE ((UINTN)MemoryMap->NumberOfPages),
  969. EFI_MEMORY_XP
  970. );
  971. break;
  972. default:
  973. SmmSetMemoryAttributes (
  974. MemoryMap->PhysicalStart,
  975. EFI_PAGES_TO_SIZE ((UINTN)MemoryMap->NumberOfPages),
  976. EFI_MEMORY_XP
  977. );
  978. break;
  979. }
  980. MemoryMap = NEXT_MEMORY_DESCRIPTOR (MemoryMap, DescriptorSize);
  981. }
  982. PatchSmmSaveStateMap ();
  983. PatchGdtIdtMap ();
  984. return;
  985. }
  986. /**
  987. Sort memory map entries based upon PhysicalStart, from low to high.
  988. @param MemoryMap A pointer to the buffer in which firmware places
  989. the current memory map.
  990. @param MemoryMapSize Size, in bytes, of the MemoryMap buffer.
  991. @param DescriptorSize Size, in bytes, of an individual EFI_MEMORY_DESCRIPTOR.
  992. **/
  993. STATIC
  994. VOID
  995. SortMemoryMap (
  996. IN OUT EFI_MEMORY_DESCRIPTOR *MemoryMap,
  997. IN UINTN MemoryMapSize,
  998. IN UINTN DescriptorSize
  999. )
  1000. {
  1001. EFI_MEMORY_DESCRIPTOR *MemoryMapEntry;
  1002. EFI_MEMORY_DESCRIPTOR *NextMemoryMapEntry;
  1003. EFI_MEMORY_DESCRIPTOR *MemoryMapEnd;
  1004. EFI_MEMORY_DESCRIPTOR TempMemoryMap;
  1005. MemoryMapEntry = MemoryMap;
  1006. NextMemoryMapEntry = NEXT_MEMORY_DESCRIPTOR (MemoryMapEntry, DescriptorSize);
  1007. MemoryMapEnd = (EFI_MEMORY_DESCRIPTOR *)((UINT8 *)MemoryMap + MemoryMapSize);
  1008. while (MemoryMapEntry < MemoryMapEnd) {
  1009. while (NextMemoryMapEntry < MemoryMapEnd) {
  1010. if (MemoryMapEntry->PhysicalStart > NextMemoryMapEntry->PhysicalStart) {
  1011. CopyMem (&TempMemoryMap, MemoryMapEntry, sizeof (EFI_MEMORY_DESCRIPTOR));
  1012. CopyMem (MemoryMapEntry, NextMemoryMapEntry, sizeof (EFI_MEMORY_DESCRIPTOR));
  1013. CopyMem (NextMemoryMapEntry, &TempMemoryMap, sizeof (EFI_MEMORY_DESCRIPTOR));
  1014. }
  1015. NextMemoryMapEntry = NEXT_MEMORY_DESCRIPTOR (NextMemoryMapEntry, DescriptorSize);
  1016. }
  1017. MemoryMapEntry = NEXT_MEMORY_DESCRIPTOR (MemoryMapEntry, DescriptorSize);
  1018. NextMemoryMapEntry = NEXT_MEMORY_DESCRIPTOR (MemoryMapEntry, DescriptorSize);
  1019. }
  1020. }
  1021. /**
  1022. Return if a UEFI memory page should be marked as not present in SMM page table.
  1023. If the memory map entries type is
  1024. EfiLoaderCode/Data, EfiBootServicesCode/Data, EfiConventionalMemory,
  1025. EfiUnusableMemory, EfiACPIReclaimMemory, return TRUE.
  1026. Or return FALSE.
  1027. @param[in] MemoryMap A pointer to the memory descriptor.
  1028. @return TRUE The memory described will be marked as not present in SMM page table.
  1029. @return FALSE The memory described will not be marked as not present in SMM page table.
  1030. **/
  1031. BOOLEAN
  1032. IsUefiPageNotPresent (
  1033. IN EFI_MEMORY_DESCRIPTOR *MemoryMap
  1034. )
  1035. {
  1036. switch (MemoryMap->Type) {
  1037. case EfiLoaderCode:
  1038. case EfiLoaderData:
  1039. case EfiBootServicesCode:
  1040. case EfiBootServicesData:
  1041. case EfiConventionalMemory:
  1042. case EfiUnusableMemory:
  1043. case EfiACPIReclaimMemory:
  1044. return TRUE;
  1045. default:
  1046. return FALSE;
  1047. }
  1048. }
  1049. /**
  1050. Merge continuous memory map entries whose type is
  1051. EfiLoaderCode/Data, EfiBootServicesCode/Data, EfiConventionalMemory,
  1052. EfiUnusableMemory, EfiACPIReclaimMemory, because the memory described by
  1053. these entries will be set as NOT present in SMM page table.
  1054. @param[in, out] MemoryMap A pointer to the buffer in which firmware places
  1055. the current memory map.
  1056. @param[in, out] MemoryMapSize A pointer to the size, in bytes, of the
  1057. MemoryMap buffer. On input, this is the size of
  1058. the current memory map. On output,
  1059. it is the size of new memory map after merge.
  1060. @param[in] DescriptorSize Size, in bytes, of an individual EFI_MEMORY_DESCRIPTOR.
  1061. **/
  1062. STATIC
  1063. VOID
  1064. MergeMemoryMapForNotPresentEntry (
  1065. IN OUT EFI_MEMORY_DESCRIPTOR *MemoryMap,
  1066. IN OUT UINTN *MemoryMapSize,
  1067. IN UINTN DescriptorSize
  1068. )
  1069. {
  1070. EFI_MEMORY_DESCRIPTOR *MemoryMapEntry;
  1071. EFI_MEMORY_DESCRIPTOR *MemoryMapEnd;
  1072. UINT64 MemoryBlockLength;
  1073. EFI_MEMORY_DESCRIPTOR *NewMemoryMapEntry;
  1074. EFI_MEMORY_DESCRIPTOR *NextMemoryMapEntry;
  1075. MemoryMapEntry = MemoryMap;
  1076. NewMemoryMapEntry = MemoryMap;
  1077. MemoryMapEnd = (EFI_MEMORY_DESCRIPTOR *)((UINT8 *)MemoryMap + *MemoryMapSize);
  1078. while ((UINTN)MemoryMapEntry < (UINTN)MemoryMapEnd) {
  1079. CopyMem (NewMemoryMapEntry, MemoryMapEntry, sizeof (EFI_MEMORY_DESCRIPTOR));
  1080. NextMemoryMapEntry = NEXT_MEMORY_DESCRIPTOR (MemoryMapEntry, DescriptorSize);
  1081. do {
  1082. MemoryBlockLength = (UINT64)(EFI_PAGES_TO_SIZE ((UINTN)MemoryMapEntry->NumberOfPages));
  1083. if (((UINTN)NextMemoryMapEntry < (UINTN)MemoryMapEnd) &&
  1084. IsUefiPageNotPresent (MemoryMapEntry) && IsUefiPageNotPresent (NextMemoryMapEntry) &&
  1085. ((MemoryMapEntry->PhysicalStart + MemoryBlockLength) == NextMemoryMapEntry->PhysicalStart))
  1086. {
  1087. MemoryMapEntry->NumberOfPages += NextMemoryMapEntry->NumberOfPages;
  1088. if (NewMemoryMapEntry != MemoryMapEntry) {
  1089. NewMemoryMapEntry->NumberOfPages += NextMemoryMapEntry->NumberOfPages;
  1090. }
  1091. NextMemoryMapEntry = NEXT_MEMORY_DESCRIPTOR (NextMemoryMapEntry, DescriptorSize);
  1092. continue;
  1093. } else {
  1094. MemoryMapEntry = PREVIOUS_MEMORY_DESCRIPTOR (NextMemoryMapEntry, DescriptorSize);
  1095. break;
  1096. }
  1097. } while (TRUE);
  1098. MemoryMapEntry = NEXT_MEMORY_DESCRIPTOR (MemoryMapEntry, DescriptorSize);
  1099. NewMemoryMapEntry = NEXT_MEMORY_DESCRIPTOR (NewMemoryMapEntry, DescriptorSize);
  1100. }
  1101. *MemoryMapSize = (UINTN)NewMemoryMapEntry - (UINTN)MemoryMap;
  1102. return;
  1103. }
  1104. /**
  1105. This function caches the GCD memory map information.
  1106. **/
  1107. VOID
  1108. GetGcdMemoryMap (
  1109. VOID
  1110. )
  1111. {
  1112. UINTN NumberOfDescriptors;
  1113. EFI_GCD_MEMORY_SPACE_DESCRIPTOR *MemSpaceMap;
  1114. EFI_STATUS Status;
  1115. UINTN Index;
  1116. Status = gDS->GetMemorySpaceMap (&NumberOfDescriptors, &MemSpaceMap);
  1117. if (EFI_ERROR (Status)) {
  1118. return;
  1119. }
  1120. mGcdMemNumberOfDesc = 0;
  1121. for (Index = 0; Index < NumberOfDescriptors; Index++) {
  1122. if ((MemSpaceMap[Index].GcdMemoryType == EfiGcdMemoryTypeReserved) &&
  1123. ((MemSpaceMap[Index].Capabilities & (EFI_MEMORY_PRESENT | EFI_MEMORY_INITIALIZED | EFI_MEMORY_TESTED)) ==
  1124. (EFI_MEMORY_PRESENT | EFI_MEMORY_INITIALIZED))
  1125. )
  1126. {
  1127. mGcdMemNumberOfDesc++;
  1128. }
  1129. }
  1130. mGcdMemSpace = AllocateZeroPool (mGcdMemNumberOfDesc * sizeof (EFI_GCD_MEMORY_SPACE_DESCRIPTOR));
  1131. ASSERT (mGcdMemSpace != NULL);
  1132. if (mGcdMemSpace == NULL) {
  1133. mGcdMemNumberOfDesc = 0;
  1134. gBS->FreePool (MemSpaceMap);
  1135. return;
  1136. }
  1137. mGcdMemNumberOfDesc = 0;
  1138. for (Index = 0; Index < NumberOfDescriptors; Index++) {
  1139. if ((MemSpaceMap[Index].GcdMemoryType == EfiGcdMemoryTypeReserved) &&
  1140. ((MemSpaceMap[Index].Capabilities & (EFI_MEMORY_PRESENT | EFI_MEMORY_INITIALIZED | EFI_MEMORY_TESTED)) ==
  1141. (EFI_MEMORY_PRESENT | EFI_MEMORY_INITIALIZED))
  1142. )
  1143. {
  1144. CopyMem (
  1145. &mGcdMemSpace[mGcdMemNumberOfDesc],
  1146. &MemSpaceMap[Index],
  1147. sizeof (EFI_GCD_MEMORY_SPACE_DESCRIPTOR)
  1148. );
  1149. mGcdMemNumberOfDesc++;
  1150. }
  1151. }
  1152. gBS->FreePool (MemSpaceMap);
  1153. }
  1154. /**
  1155. Get UEFI MemoryAttributesTable.
  1156. **/
  1157. VOID
  1158. GetUefiMemoryAttributesTable (
  1159. VOID
  1160. )
  1161. {
  1162. EFI_STATUS Status;
  1163. EFI_MEMORY_ATTRIBUTES_TABLE *MemoryAttributesTable;
  1164. UINTN MemoryAttributesTableSize;
  1165. Status = EfiGetSystemConfigurationTable (&gEfiMemoryAttributesTableGuid, (VOID **)&MemoryAttributesTable);
  1166. if (!EFI_ERROR (Status) && (MemoryAttributesTable != NULL)) {
  1167. MemoryAttributesTableSize = sizeof (EFI_MEMORY_ATTRIBUTES_TABLE) + MemoryAttributesTable->DescriptorSize * MemoryAttributesTable->NumberOfEntries;
  1168. mUefiMemoryAttributesTable = AllocateCopyPool (MemoryAttributesTableSize, MemoryAttributesTable);
  1169. ASSERT (mUefiMemoryAttributesTable != NULL);
  1170. }
  1171. }
  1172. /**
  1173. This function caches the UEFI memory map information.
  1174. **/
  1175. VOID
  1176. GetUefiMemoryMap (
  1177. VOID
  1178. )
  1179. {
  1180. EFI_STATUS Status;
  1181. UINTN MapKey;
  1182. UINT32 DescriptorVersion;
  1183. EFI_MEMORY_DESCRIPTOR *MemoryMap;
  1184. UINTN UefiMemoryMapSize;
  1185. DEBUG ((DEBUG_INFO, "GetUefiMemoryMap\n"));
  1186. UefiMemoryMapSize = 0;
  1187. MemoryMap = NULL;
  1188. Status = gBS->GetMemoryMap (
  1189. &UefiMemoryMapSize,
  1190. MemoryMap,
  1191. &MapKey,
  1192. &mUefiDescriptorSize,
  1193. &DescriptorVersion
  1194. );
  1195. ASSERT (Status == EFI_BUFFER_TOO_SMALL);
  1196. do {
  1197. Status = gBS->AllocatePool (EfiBootServicesData, UefiMemoryMapSize, (VOID **)&MemoryMap);
  1198. ASSERT (MemoryMap != NULL);
  1199. if (MemoryMap == NULL) {
  1200. return;
  1201. }
  1202. Status = gBS->GetMemoryMap (
  1203. &UefiMemoryMapSize,
  1204. MemoryMap,
  1205. &MapKey,
  1206. &mUefiDescriptorSize,
  1207. &DescriptorVersion
  1208. );
  1209. if (EFI_ERROR (Status)) {
  1210. gBS->FreePool (MemoryMap);
  1211. MemoryMap = NULL;
  1212. }
  1213. } while (Status == EFI_BUFFER_TOO_SMALL);
  1214. if (MemoryMap == NULL) {
  1215. return;
  1216. }
  1217. SortMemoryMap (MemoryMap, UefiMemoryMapSize, mUefiDescriptorSize);
  1218. MergeMemoryMapForNotPresentEntry (MemoryMap, &UefiMemoryMapSize, mUefiDescriptorSize);
  1219. mUefiMemoryMapSize = UefiMemoryMapSize;
  1220. mUefiMemoryMap = AllocateCopyPool (UefiMemoryMapSize, MemoryMap);
  1221. ASSERT (mUefiMemoryMap != NULL);
  1222. gBS->FreePool (MemoryMap);
  1223. //
  1224. // Get additional information from GCD memory map.
  1225. //
  1226. GetGcdMemoryMap ();
  1227. //
  1228. // Get UEFI memory attributes table.
  1229. //
  1230. GetUefiMemoryAttributesTable ();
  1231. }
  1232. /**
  1233. This function sets UEFI memory attribute according to UEFI memory map.
  1234. The normal memory region is marked as not present, such as
  1235. EfiLoaderCode/Data, EfiBootServicesCode/Data, EfiConventionalMemory,
  1236. EfiUnusableMemory, EfiACPIReclaimMemory.
  1237. **/
  1238. VOID
  1239. SetUefiMemMapAttributes (
  1240. VOID
  1241. )
  1242. {
  1243. EFI_STATUS Status;
  1244. EFI_MEMORY_DESCRIPTOR *MemoryMap;
  1245. UINTN MemoryMapEntryCount;
  1246. UINTN Index;
  1247. EFI_MEMORY_DESCRIPTOR *Entry;
  1248. DEBUG ((DEBUG_INFO, "SetUefiMemMapAttributes\n"));
  1249. if (mUefiMemoryMap != NULL) {
  1250. MemoryMapEntryCount = mUefiMemoryMapSize/mUefiDescriptorSize;
  1251. MemoryMap = mUefiMemoryMap;
  1252. for (Index = 0; Index < MemoryMapEntryCount; Index++) {
  1253. if (IsUefiPageNotPresent (MemoryMap)) {
  1254. Status = SmmSetMemoryAttributes (
  1255. MemoryMap->PhysicalStart,
  1256. EFI_PAGES_TO_SIZE ((UINTN)MemoryMap->NumberOfPages),
  1257. EFI_MEMORY_RP
  1258. );
  1259. DEBUG ((
  1260. DEBUG_INFO,
  1261. "UefiMemory protection: 0x%lx - 0x%lx %r\n",
  1262. MemoryMap->PhysicalStart,
  1263. MemoryMap->PhysicalStart + (UINT64)EFI_PAGES_TO_SIZE ((UINTN)MemoryMap->NumberOfPages),
  1264. Status
  1265. ));
  1266. }
  1267. MemoryMap = NEXT_MEMORY_DESCRIPTOR (MemoryMap, mUefiDescriptorSize);
  1268. }
  1269. }
  1270. //
  1271. // Do not free mUefiMemoryMap, it will be checked in IsSmmCommBufferForbiddenAddress().
  1272. //
  1273. //
  1274. // Set untested memory as not present.
  1275. //
  1276. if (mGcdMemSpace != NULL) {
  1277. for (Index = 0; Index < mGcdMemNumberOfDesc; Index++) {
  1278. Status = SmmSetMemoryAttributes (
  1279. mGcdMemSpace[Index].BaseAddress,
  1280. mGcdMemSpace[Index].Length,
  1281. EFI_MEMORY_RP
  1282. );
  1283. DEBUG ((
  1284. DEBUG_INFO,
  1285. "GcdMemory protection: 0x%lx - 0x%lx %r\n",
  1286. mGcdMemSpace[Index].BaseAddress,
  1287. mGcdMemSpace[Index].BaseAddress + mGcdMemSpace[Index].Length,
  1288. Status
  1289. ));
  1290. }
  1291. }
  1292. //
  1293. // Do not free mGcdMemSpace, it will be checked in IsSmmCommBufferForbiddenAddress().
  1294. //
  1295. //
  1296. // Set UEFI runtime memory with EFI_MEMORY_RO as not present.
  1297. //
  1298. if (mUefiMemoryAttributesTable != NULL) {
  1299. Entry = (EFI_MEMORY_DESCRIPTOR *)(mUefiMemoryAttributesTable + 1);
  1300. for (Index = 0; Index < mUefiMemoryAttributesTable->NumberOfEntries; Index++) {
  1301. if ((Entry->Type == EfiRuntimeServicesCode) || (Entry->Type == EfiRuntimeServicesData)) {
  1302. if ((Entry->Attribute & EFI_MEMORY_RO) != 0) {
  1303. Status = SmmSetMemoryAttributes (
  1304. Entry->PhysicalStart,
  1305. EFI_PAGES_TO_SIZE ((UINTN)Entry->NumberOfPages),
  1306. EFI_MEMORY_RP
  1307. );
  1308. DEBUG ((
  1309. DEBUG_INFO,
  1310. "UefiMemoryAttribute protection: 0x%lx - 0x%lx %r\n",
  1311. Entry->PhysicalStart,
  1312. Entry->PhysicalStart + (UINT64)EFI_PAGES_TO_SIZE ((UINTN)Entry->NumberOfPages),
  1313. Status
  1314. ));
  1315. }
  1316. }
  1317. Entry = NEXT_MEMORY_DESCRIPTOR (Entry, mUefiMemoryAttributesTable->DescriptorSize);
  1318. }
  1319. }
  1320. //
  1321. // Do not free mUefiMemoryAttributesTable, it will be checked in IsSmmCommBufferForbiddenAddress().
  1322. //
  1323. }
  1324. /**
  1325. Return if the Address is forbidden as SMM communication buffer.
  1326. @param[in] Address the address to be checked
  1327. @return TRUE The address is forbidden as SMM communication buffer.
  1328. @return FALSE The address is allowed as SMM communication buffer.
  1329. **/
  1330. BOOLEAN
  1331. IsSmmCommBufferForbiddenAddress (
  1332. IN UINT64 Address
  1333. )
  1334. {
  1335. EFI_MEMORY_DESCRIPTOR *MemoryMap;
  1336. UINTN MemoryMapEntryCount;
  1337. UINTN Index;
  1338. EFI_MEMORY_DESCRIPTOR *Entry;
  1339. if (mUefiMemoryMap != NULL) {
  1340. MemoryMap = mUefiMemoryMap;
  1341. MemoryMapEntryCount = mUefiMemoryMapSize/mUefiDescriptorSize;
  1342. for (Index = 0; Index < MemoryMapEntryCount; Index++) {
  1343. if (IsUefiPageNotPresent (MemoryMap)) {
  1344. if ((Address >= MemoryMap->PhysicalStart) &&
  1345. (Address < MemoryMap->PhysicalStart + EFI_PAGES_TO_SIZE ((UINTN)MemoryMap->NumberOfPages)))
  1346. {
  1347. return TRUE;
  1348. }
  1349. }
  1350. MemoryMap = NEXT_MEMORY_DESCRIPTOR (MemoryMap, mUefiDescriptorSize);
  1351. }
  1352. }
  1353. if (mGcdMemSpace != NULL) {
  1354. for (Index = 0; Index < mGcdMemNumberOfDesc; Index++) {
  1355. if ((Address >= mGcdMemSpace[Index].BaseAddress) &&
  1356. (Address < mGcdMemSpace[Index].BaseAddress + mGcdMemSpace[Index].Length))
  1357. {
  1358. return TRUE;
  1359. }
  1360. }
  1361. }
  1362. if (mUefiMemoryAttributesTable != NULL) {
  1363. Entry = (EFI_MEMORY_DESCRIPTOR *)(mUefiMemoryAttributesTable + 1);
  1364. for (Index = 0; Index < mUefiMemoryAttributesTable->NumberOfEntries; Index++) {
  1365. if ((Entry->Type == EfiRuntimeServicesCode) || (Entry->Type == EfiRuntimeServicesData)) {
  1366. if ((Entry->Attribute & EFI_MEMORY_RO) != 0) {
  1367. if ((Address >= Entry->PhysicalStart) &&
  1368. (Address < Entry->PhysicalStart + LShiftU64 (Entry->NumberOfPages, EFI_PAGE_SHIFT)))
  1369. {
  1370. return TRUE;
  1371. }
  1372. Entry = NEXT_MEMORY_DESCRIPTOR (Entry, mUefiMemoryAttributesTable->DescriptorSize);
  1373. }
  1374. }
  1375. }
  1376. }
  1377. return FALSE;
  1378. }
  1379. /**
  1380. This function set given attributes of the memory region specified by
  1381. BaseAddress and Length.
  1382. @param This The EDKII_SMM_MEMORY_ATTRIBUTE_PROTOCOL instance.
  1383. @param BaseAddress The physical address that is the start address of
  1384. a memory region.
  1385. @param Length The size in bytes of the memory region.
  1386. @param Attributes The bit mask of attributes to set for the memory
  1387. region.
  1388. @retval EFI_SUCCESS The attributes were set for the memory region.
  1389. @retval EFI_INVALID_PARAMETER Length is zero.
  1390. Attributes specified an illegal combination of
  1391. attributes that cannot be set together.
  1392. @retval EFI_UNSUPPORTED The processor does not support one or more
  1393. bytes of the memory resource range specified
  1394. by BaseAddress and Length.
  1395. The bit mask of attributes is not supported for
  1396. the memory resource range specified by
  1397. BaseAddress and Length.
  1398. **/
  1399. EFI_STATUS
  1400. EFIAPI
  1401. EdkiiSmmSetMemoryAttributes (
  1402. IN EDKII_SMM_MEMORY_ATTRIBUTE_PROTOCOL *This,
  1403. IN EFI_PHYSICAL_ADDRESS BaseAddress,
  1404. IN UINT64 Length,
  1405. IN UINT64 Attributes
  1406. )
  1407. {
  1408. return SmmSetMemoryAttributes (BaseAddress, Length, Attributes);
  1409. }
  1410. /**
  1411. This function clears given attributes of the memory region specified by
  1412. BaseAddress and Length.
  1413. @param This The EDKII_SMM_MEMORY_ATTRIBUTE_PROTOCOL instance.
  1414. @param BaseAddress The physical address that is the start address of
  1415. a memory region.
  1416. @param Length The size in bytes of the memory region.
  1417. @param Attributes The bit mask of attributes to clear for the memory
  1418. region.
  1419. @retval EFI_SUCCESS The attributes were cleared for the memory region.
  1420. @retval EFI_INVALID_PARAMETER Length is zero.
  1421. Attributes specified an illegal combination of
  1422. attributes that cannot be cleared together.
  1423. @retval EFI_UNSUPPORTED The processor does not support one or more
  1424. bytes of the memory resource range specified
  1425. by BaseAddress and Length.
  1426. The bit mask of attributes is not supported for
  1427. the memory resource range specified by
  1428. BaseAddress and Length.
  1429. **/
  1430. EFI_STATUS
  1431. EFIAPI
  1432. EdkiiSmmClearMemoryAttributes (
  1433. IN EDKII_SMM_MEMORY_ATTRIBUTE_PROTOCOL *This,
  1434. IN EFI_PHYSICAL_ADDRESS BaseAddress,
  1435. IN UINT64 Length,
  1436. IN UINT64 Attributes
  1437. )
  1438. {
  1439. return SmmClearMemoryAttributes (BaseAddress, Length, Attributes);
  1440. }
  1441. /**
  1442. This function retrieves the attributes of the memory region specified by
  1443. BaseAddress and Length. If different attributes are got from different part
  1444. of the memory region, EFI_NO_MAPPING will be returned.
  1445. @param This The EDKII_SMM_MEMORY_ATTRIBUTE_PROTOCOL instance.
  1446. @param BaseAddress The physical address that is the start address of
  1447. a memory region.
  1448. @param Length The size in bytes of the memory region.
  1449. @param Attributes Pointer to attributes returned.
  1450. @retval EFI_SUCCESS The attributes got for the memory region.
  1451. @retval EFI_INVALID_PARAMETER Length is zero.
  1452. Attributes is NULL.
  1453. @retval EFI_NO_MAPPING Attributes are not consistent cross the memory
  1454. region.
  1455. @retval EFI_UNSUPPORTED The processor does not support one or more
  1456. bytes of the memory resource range specified
  1457. by BaseAddress and Length.
  1458. **/
  1459. EFI_STATUS
  1460. EFIAPI
  1461. EdkiiSmmGetMemoryAttributes (
  1462. IN EDKII_SMM_MEMORY_ATTRIBUTE_PROTOCOL *This,
  1463. IN EFI_PHYSICAL_ADDRESS BaseAddress,
  1464. IN UINT64 Length,
  1465. OUT UINT64 *Attributes
  1466. )
  1467. {
  1468. EFI_PHYSICAL_ADDRESS Address;
  1469. UINT64 *PageEntry;
  1470. UINT64 MemAttr;
  1471. PAGE_ATTRIBUTE PageAttr;
  1472. INT64 Size;
  1473. UINTN PageTableBase;
  1474. BOOLEAN EnablePML5Paging;
  1475. IA32_CR4 Cr4;
  1476. if ((Length < SIZE_4KB) || (Attributes == NULL)) {
  1477. return EFI_INVALID_PARAMETER;
  1478. }
  1479. Size = (INT64)Length;
  1480. MemAttr = (UINT64)-1;
  1481. PageTableBase = AsmReadCr3 () & PAGING_4K_ADDRESS_MASK_64;
  1482. Cr4.UintN = AsmReadCr4 ();
  1483. EnablePML5Paging = (BOOLEAN)(Cr4.Bits.LA57 == 1);
  1484. do {
  1485. PageEntry = GetPageTableEntry (PageTableBase, EnablePML5Paging, BaseAddress, &PageAttr);
  1486. if ((PageEntry == NULL) || (PageAttr == PageNone)) {
  1487. return EFI_UNSUPPORTED;
  1488. }
  1489. //
  1490. // If the memory range is cross page table boundary, make sure they
  1491. // share the same attribute. Return EFI_NO_MAPPING if not.
  1492. //
  1493. *Attributes = GetAttributesFromPageEntry (PageEntry);
  1494. if ((MemAttr != (UINT64)-1) && (*Attributes != MemAttr)) {
  1495. return EFI_NO_MAPPING;
  1496. }
  1497. switch (PageAttr) {
  1498. case Page4K:
  1499. Address = *PageEntry & ~mAddressEncMask & PAGING_4K_ADDRESS_MASK_64;
  1500. Size -= (SIZE_4KB - (BaseAddress - Address));
  1501. BaseAddress += (SIZE_4KB - (BaseAddress - Address));
  1502. break;
  1503. case Page2M:
  1504. Address = *PageEntry & ~mAddressEncMask & PAGING_2M_ADDRESS_MASK_64;
  1505. Size -= SIZE_2MB - (BaseAddress - Address);
  1506. BaseAddress += SIZE_2MB - (BaseAddress - Address);
  1507. break;
  1508. case Page1G:
  1509. Address = *PageEntry & ~mAddressEncMask & PAGING_1G_ADDRESS_MASK_64;
  1510. Size -= SIZE_1GB - (BaseAddress - Address);
  1511. BaseAddress += SIZE_1GB - (BaseAddress - Address);
  1512. break;
  1513. default:
  1514. return EFI_UNSUPPORTED;
  1515. }
  1516. MemAttr = *Attributes;
  1517. } while (Size > 0);
  1518. return EFI_SUCCESS;
  1519. }
  1520. /**
  1521. Prevent the memory pages used for SMM page table from been overwritten.
  1522. **/
  1523. VOID
  1524. EnablePageTableProtection (
  1525. VOID
  1526. )
  1527. {
  1528. PAGE_TABLE_POOL *HeadPool;
  1529. PAGE_TABLE_POOL *Pool;
  1530. UINT64 PoolSize;
  1531. EFI_PHYSICAL_ADDRESS Address;
  1532. UINTN PageTableBase;
  1533. if (mPageTablePool == NULL) {
  1534. return;
  1535. }
  1536. PageTableBase = AsmReadCr3 () & PAGING_4K_ADDRESS_MASK_64;
  1537. //
  1538. // ConvertMemoryPageAttributes might update mPageTablePool. It's safer to
  1539. // remember original one in advance.
  1540. //
  1541. HeadPool = mPageTablePool;
  1542. Pool = HeadPool;
  1543. do {
  1544. Address = (EFI_PHYSICAL_ADDRESS)(UINTN)Pool;
  1545. PoolSize = Pool->Offset + EFI_PAGES_TO_SIZE (Pool->FreePages);
  1546. //
  1547. // Set entire pool including header, used-memory and left free-memory as ReadOnly in SMM page table.
  1548. //
  1549. ConvertMemoryPageAttributes (PageTableBase, m5LevelPagingNeeded, Address, PoolSize, EFI_MEMORY_RO, TRUE, NULL, NULL);
  1550. Pool = Pool->NextPool;
  1551. } while (Pool != HeadPool);
  1552. }
  1553. /**
  1554. Return whether memory used by SMM page table need to be set as Read Only.
  1555. @retval TRUE Need to set SMM page table as Read Only.
  1556. @retval FALSE Do not set SMM page table as Read Only.
  1557. **/
  1558. BOOLEAN
  1559. IfReadOnlyPageTableNeeded (
  1560. VOID
  1561. )
  1562. {
  1563. //
  1564. // Don't mark page table memory as read-only if
  1565. // - no restriction on access to non-SMRAM memory; or
  1566. // - SMM heap guard feature enabled; or
  1567. // BIT2: SMM page guard enabled
  1568. // BIT3: SMM pool guard enabled
  1569. // - SMM profile feature enabled
  1570. //
  1571. if (!IsRestrictedMemoryAccess () ||
  1572. ((PcdGet8 (PcdHeapGuardPropertyMask) & (BIT3 | BIT2)) != 0) ||
  1573. FeaturePcdGet (PcdCpuSmmProfileEnable))
  1574. {
  1575. if (sizeof (UINTN) == sizeof (UINT64)) {
  1576. //
  1577. // Restriction on access to non-SMRAM memory and heap guard could not be enabled at the same time.
  1578. //
  1579. ASSERT (
  1580. !(IsRestrictedMemoryAccess () &&
  1581. (PcdGet8 (PcdHeapGuardPropertyMask) & (BIT3 | BIT2)) != 0)
  1582. );
  1583. //
  1584. // Restriction on access to non-SMRAM memory and SMM profile could not be enabled at the same time.
  1585. //
  1586. ASSERT (!(IsRestrictedMemoryAccess () && FeaturePcdGet (PcdCpuSmmProfileEnable)));
  1587. }
  1588. return FALSE;
  1589. }
  1590. return TRUE;
  1591. }
  1592. /**
  1593. This function sets memory attribute for page table.
  1594. **/
  1595. VOID
  1596. SetPageTableAttributes (
  1597. VOID
  1598. )
  1599. {
  1600. BOOLEAN CetEnabled;
  1601. if (!IfReadOnlyPageTableNeeded ()) {
  1602. return;
  1603. }
  1604. DEBUG ((DEBUG_INFO, "SetPageTableAttributes\n"));
  1605. //
  1606. // Disable write protection, because we need mark page table to be write protected.
  1607. // We need *write* page table memory, to mark itself to be *read only*.
  1608. //
  1609. CetEnabled = ((AsmReadCr4 () & CR4_CET_ENABLE) != 0) ? TRUE : FALSE;
  1610. if (CetEnabled) {
  1611. //
  1612. // CET must be disabled if WP is disabled.
  1613. //
  1614. DisableCet ();
  1615. }
  1616. AsmWriteCr0 (AsmReadCr0 () & ~CR0_WP);
  1617. // Set memory used by page table as Read Only.
  1618. DEBUG ((DEBUG_INFO, "Start...\n"));
  1619. EnablePageTableProtection ();
  1620. //
  1621. // Enable write protection, after page table attribute updated.
  1622. //
  1623. AsmWriteCr0 (AsmReadCr0 () | CR0_WP);
  1624. mIsReadOnlyPageTable = TRUE;
  1625. //
  1626. // Flush TLB after mark all page table pool as read only.
  1627. //
  1628. FlushTlbForAll ();
  1629. if (CetEnabled) {
  1630. //
  1631. // re-enable CET.
  1632. //
  1633. EnableCet ();
  1634. }
  1635. return;
  1636. }