PrmConfigDxe.c 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512
  1. /** @file
  2. This file contains the implementation for a Platform Runtime Mechanism (PRM) configuration driver.
  3. Copyright (c) Microsoft Corporation
  4. Copyright (c) 2020, Intel Corporation. All rights reserved.<BR>
  5. SPDX-License-Identifier: BSD-2-Clause-Patent
  6. **/
  7. #include <Library/BaseLib.h>
  8. #include <Library/BaseMemoryLib.h>
  9. #include <Library/DebugLib.h>
  10. #include <Library/DxeServicesTableLib.h>
  11. #include <Library/MemoryAllocationLib.h>
  12. #include <Library/UefiBootServicesTableLib.h>
  13. #include <Library/UefiRuntimeServicesTableLib.h>
  14. #include <Library/UefiLib.h>
  15. #include <PiDxe.h>
  16. #include <PrmContextBuffer.h>
  17. #include <PrmDataBuffer.h>
  18. #include <PrmMmio.h>
  19. #include <Protocol/PrmConfig.h>
  20. #define _DBGMSGID_ "[PRMCONFIG]"
  21. STATIC UINTN mMaxRuntimeMmioRangeCount;
  22. GLOBAL_REMOVE_IF_UNREFERENCED STATIC PRM_RUNTIME_MMIO_RANGES **mRuntimeMmioRanges;
  23. /**
  24. Converts the runtime memory range physical addresses to virtual addresses.
  25. @param[in] RuntimeMmioRanges A pointer to a PRM_RUNTIME_MMIO_RANGES buffer.
  26. **/
  27. VOID
  28. ConvertRuntimeMemoryRangeAddresses (
  29. IN PRM_RUNTIME_MMIO_RANGES *RuntimeMmioRanges
  30. )
  31. {
  32. UINTN Index;
  33. if ((RuntimeMmioRanges == NULL) || (RuntimeMmioRanges->Count == 0)) {
  34. return;
  35. }
  36. for (Index = 0; Index < (UINTN)RuntimeMmioRanges->Count; Index++) {
  37. RuntimeMmioRanges->Range[Index].VirtualBaseAddress = RuntimeMmioRanges->Range[Index].PhysicalBaseAddress;
  38. gRT->ConvertPointer (0x0, (VOID **)&(RuntimeMmioRanges->Range[Index].VirtualBaseAddress));
  39. }
  40. }
  41. /**
  42. Sets the runtime memory range attributes.
  43. The EFI_MEMORY_RUNTIME attribute is set for each PRM_RUNTIME_MMIO_RANGE present
  44. in the buffer provided.
  45. @param[in] RuntimeMmioRanges A pointer to a PRM_RUNTIME_MMIO_RANGES buffer.
  46. **/
  47. VOID
  48. SetRuntimeMemoryRangeAttributes (
  49. IN PRM_RUNTIME_MMIO_RANGES *RuntimeMmioRanges
  50. )
  51. {
  52. EFI_STATUS Status;
  53. EFI_STATUS Status2;
  54. UINTN Index;
  55. EFI_GCD_MEMORY_SPACE_DESCRIPTOR Descriptor;
  56. DEBUG ((DEBUG_INFO, "%a %a - Entry.\n", _DBGMSGID_, __FUNCTION__));
  57. if ((RuntimeMmioRanges == NULL) || (RuntimeMmioRanges->Count == 0)) {
  58. return;
  59. }
  60. for (Index = 0; Index < (UINTN)RuntimeMmioRanges->Count; Index++) {
  61. DEBUG ((
  62. DEBUG_INFO,
  63. " %a %a: Runtime MMIO Range [%d].\n",
  64. _DBGMSGID_,
  65. __FUNCTION__,
  66. Index
  67. ));
  68. DEBUG ((
  69. DEBUG_INFO,
  70. " %a %a: Physical address = 0x%016x. Length = 0x%x.\n",
  71. _DBGMSGID_,
  72. __FUNCTION__,
  73. RuntimeMmioRanges->Range[Index].PhysicalBaseAddress,
  74. RuntimeMmioRanges->Range[Index].Length
  75. ));
  76. // Runtime memory ranges should cover ranges on a page boundary
  77. ASSERT ((RuntimeMmioRanges->Range[Index].PhysicalBaseAddress & EFI_PAGE_MASK) == 0);
  78. ASSERT ((RuntimeMmioRanges->Range[Index].Length & EFI_PAGE_MASK) == 0);
  79. Status2 = EFI_NOT_FOUND;
  80. Status = gDS->GetMemorySpaceDescriptor (RuntimeMmioRanges->Range[Index].PhysicalBaseAddress, &Descriptor);
  81. if (!EFI_ERROR (Status) &&
  82. (
  83. ((Descriptor.GcdMemoryType != EfiGcdMemoryTypeMemoryMappedIo) && (Descriptor.GcdMemoryType != EfiGcdMemoryTypeReserved)) ||
  84. ((Descriptor.Length & EFI_PAGE_MASK) != 0)
  85. )
  86. )
  87. {
  88. Status2 = gDS->RemoveMemorySpace (
  89. RuntimeMmioRanges->Range[Index].PhysicalBaseAddress,
  90. Descriptor.Length
  91. );
  92. }
  93. if ((Status == EFI_NOT_FOUND) || !EFI_ERROR (Status2)) {
  94. Status = gDS->AddMemorySpace (
  95. EfiGcdMemoryTypeMemoryMappedIo,
  96. RuntimeMmioRanges->Range[Index].PhysicalBaseAddress,
  97. (UINT64)RuntimeMmioRanges->Range[Index].Length,
  98. EFI_MEMORY_UC | EFI_MEMORY_RUNTIME
  99. );
  100. ASSERT_EFI_ERROR (Status);
  101. Status = gDS->AllocateMemorySpace (
  102. EfiGcdAllocateAddress,
  103. EfiGcdMemoryTypeMemoryMappedIo,
  104. 0,
  105. (UINT64)RuntimeMmioRanges->Range[Index].Length,
  106. &RuntimeMmioRanges->Range[Index].PhysicalBaseAddress,
  107. gImageHandle,
  108. NULL
  109. );
  110. ASSERT_EFI_ERROR (Status);
  111. }
  112. Status = gDS->GetMemorySpaceDescriptor (RuntimeMmioRanges->Range[Index].PhysicalBaseAddress, &Descriptor);
  113. ASSERT_EFI_ERROR (Status);
  114. if (EFI_ERROR (Status)) {
  115. DEBUG ((
  116. DEBUG_ERROR,
  117. " %a %a: Error [%r] finding descriptor for runtime memory range 0x%016x.\n",
  118. _DBGMSGID_,
  119. __FUNCTION__,
  120. Status,
  121. RuntimeMmioRanges->Range[Index].PhysicalBaseAddress
  122. ));
  123. continue;
  124. }
  125. if ((Descriptor.Attributes & EFI_MEMORY_RUNTIME) != 0) {
  126. continue;
  127. }
  128. Status = gDS->SetMemorySpaceAttributes (
  129. RuntimeMmioRanges->Range[Index].PhysicalBaseAddress,
  130. (UINT64)RuntimeMmioRanges->Range[Index].Length,
  131. Descriptor.Attributes | EFI_MEMORY_RUNTIME
  132. );
  133. ASSERT_EFI_ERROR (Status);
  134. if (EFI_ERROR (Status)) {
  135. DEBUG ((
  136. DEBUG_ERROR,
  137. " %a %a: Error [%r] setting descriptor for runtime memory range 0x%016x.\n",
  138. _DBGMSGID_,
  139. __FUNCTION__,
  140. Status,
  141. RuntimeMmioRanges->Range[Index].PhysicalBaseAddress
  142. ));
  143. } else {
  144. DEBUG ((DEBUG_INFO, " %a %a: Successfully set runtime attribute for the MMIO range.\n", _DBGMSGID_, __FUNCTION__));
  145. }
  146. }
  147. }
  148. /**
  149. Stores pointers or pointer to resources that should be converted in the virtual address change event.
  150. **/
  151. VOID
  152. StoreVirtualMemoryAddressChangePointers (
  153. VOID
  154. )
  155. {
  156. EFI_STATUS Status;
  157. UINTN HandleCount;
  158. UINTN HandleIndex;
  159. UINTN RangeIndex;
  160. EFI_HANDLE *HandleBuffer;
  161. PRM_CONFIG_PROTOCOL *PrmConfigProtocol;
  162. DEBUG ((DEBUG_INFO, "%a %a - Entry.\n", _DBGMSGID_, __FUNCTION__));
  163. RangeIndex = 0;
  164. mRuntimeMmioRanges = AllocateRuntimeZeroPool (sizeof (*mRuntimeMmioRanges) * mMaxRuntimeMmioRangeCount);
  165. if ((mRuntimeMmioRanges == NULL) && (mMaxRuntimeMmioRangeCount > 0)) {
  166. DEBUG ((
  167. DEBUG_ERROR,
  168. " %a %a: Memory allocation for runtime MMIO pointer array failed.\n",
  169. _DBGMSGID_,
  170. __FUNCTION__
  171. ));
  172. ASSERT (FALSE);
  173. return;
  174. }
  175. HandleBuffer = NULL;
  176. Status = gBS->LocateHandleBuffer (
  177. ByProtocol,
  178. &gPrmConfigProtocolGuid,
  179. NULL,
  180. &HandleCount,
  181. &HandleBuffer
  182. );
  183. if (!EFI_ERROR (Status)) {
  184. for (HandleIndex = 0; HandleIndex < HandleCount; HandleIndex++) {
  185. Status = gBS->HandleProtocol (
  186. HandleBuffer[HandleIndex],
  187. &gPrmConfigProtocolGuid,
  188. (VOID **)&PrmConfigProtocol
  189. );
  190. ASSERT_EFI_ERROR (Status);
  191. if (EFI_ERROR (Status) || (PrmConfigProtocol == NULL)) {
  192. continue;
  193. }
  194. if (PrmConfigProtocol->ModuleContextBuffers.RuntimeMmioRanges != NULL) {
  195. if (RangeIndex >= mMaxRuntimeMmioRangeCount) {
  196. Status = EFI_BUFFER_TOO_SMALL;
  197. DEBUG ((
  198. DEBUG_ERROR,
  199. " %a %a: Index out of bounds - Actual count (%d) of runtime MMIO ranges exceeds maximum count (%d).\n",
  200. _DBGMSGID_,
  201. __FUNCTION__,
  202. RangeIndex + 1,
  203. mMaxRuntimeMmioRangeCount
  204. ));
  205. ASSERT_EFI_ERROR (Status);
  206. return;
  207. }
  208. mRuntimeMmioRanges[RangeIndex++] = PrmConfigProtocol->ModuleContextBuffers.RuntimeMmioRanges;
  209. }
  210. }
  211. DEBUG ((
  212. DEBUG_INFO,
  213. " %a %a: %d MMIO ranges buffers saved for future virtual memory conversion.\n",
  214. _DBGMSGID_,
  215. __FUNCTION__,
  216. RangeIndex
  217. ));
  218. }
  219. }
  220. /**
  221. Validates a data buffer for a PRM module.
  222. Verifies the buffer header signature is valid and the length meets the minimum size.
  223. @param[in] PrmDataBuffer A pointer to the data buffer for this PRM module.
  224. @retval EFI_SUCCESS The data buffer was validated successfully.
  225. @retval EFI_INVALID_PARAMETER The pointer given for PrmDataBuffer is NULL.
  226. @retval EFI_NOT_FOUND The data buffer signature is not valid.
  227. @retval EFI_BUFFER_TOO_SMALL The buffer size is too small.
  228. **/
  229. EFI_STATUS
  230. ValidatePrmDataBuffer (
  231. IN CONST PRM_DATA_BUFFER *PrmDataBuffer
  232. )
  233. {
  234. if (PrmDataBuffer == NULL) {
  235. return EFI_INVALID_PARAMETER;
  236. }
  237. if (PrmDataBuffer->Header.Signature != PRM_DATA_BUFFER_HEADER_SIGNATURE) {
  238. DEBUG ((DEBUG_ERROR, " %a %a: The PRM data buffer signature is invalid. PRM module.\n", _DBGMSGID_, __FUNCTION__));
  239. return EFI_NOT_FOUND;
  240. }
  241. if (PrmDataBuffer->Header.Length < sizeof (PRM_DATA_BUFFER_HEADER)) {
  242. DEBUG ((DEBUG_ERROR, " %a %a: The PRM data buffer length is invalid.\n", _DBGMSGID_, __FUNCTION__));
  243. return EFI_BUFFER_TOO_SMALL;
  244. }
  245. return EFI_SUCCESS;
  246. }
  247. /**
  248. Validates a PRM context buffer.
  249. Verifies the buffer header signature is valid and the GUID is set to a non-zero value.
  250. @param[in] PrmContextBuffer A pointer to the context buffer for this PRM handler.
  251. @retval EFI_SUCCESS The context buffer was validated successfully.
  252. @retval EFI_INVALID_PARAMETER The pointer given for ContextBuffer is NULL.
  253. @retval EFI_NOT_FOUND The proper value for a field was not found.
  254. **/
  255. EFI_STATUS
  256. ValidatePrmContextBuffer (
  257. IN CONST PRM_CONTEXT_BUFFER *PrmContextBuffer
  258. )
  259. {
  260. if (PrmContextBuffer == NULL) {
  261. return EFI_INVALID_PARAMETER;
  262. }
  263. if (PrmContextBuffer->Signature != PRM_CONTEXT_BUFFER_SIGNATURE) {
  264. DEBUG ((DEBUG_ERROR, " %a %a: The PRM context buffer signature is invalid.\n", _DBGMSGID_, __FUNCTION__));
  265. return EFI_NOT_FOUND;
  266. }
  267. if (IsZeroGuid (&PrmContextBuffer->HandlerGuid)) {
  268. DEBUG ((DEBUG_ERROR, " %a %a: The PRM context buffer GUID is zero.\n", _DBGMSGID_, __FUNCTION__));
  269. return EFI_NOT_FOUND;
  270. }
  271. if ((PrmContextBuffer->StaticDataBuffer != NULL) && EFI_ERROR (ValidatePrmDataBuffer (PrmContextBuffer->StaticDataBuffer))) {
  272. DEBUG ((
  273. DEBUG_ERROR,
  274. " %a %a: Error in static buffer for PRM handler %g.\n",
  275. _DBGMSGID_,
  276. __FUNCTION__,
  277. &PrmContextBuffer->HandlerGuid
  278. ));
  279. return EFI_NOT_FOUND;
  280. }
  281. return EFI_SUCCESS;
  282. }
  283. /**
  284. Notification function of EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE.
  285. This is notification function converts any registered PRM_RUNTIME_MMIO_RANGE
  286. addresses to a virtual address.
  287. @param[in] Event Event whose notification function is being invoked.
  288. @param[in] Context Pointer to the notification function's context.
  289. **/
  290. VOID
  291. EFIAPI
  292. PrmConfigVirtualAddressChangeEvent (
  293. IN EFI_EVENT Event,
  294. IN VOID *Context
  295. )
  296. {
  297. UINTN Index;
  298. //
  299. // Convert runtime MMIO ranges
  300. //
  301. for (Index = 0; Index < mMaxRuntimeMmioRangeCount; Index++) {
  302. ConvertRuntimeMemoryRangeAddresses (mRuntimeMmioRanges[Index]);
  303. }
  304. }
  305. /**
  306. The PRM Config END_OF_DXE protocol notification event handler.
  307. Finds all of the PRM_CONFIG_PROTOCOL instances installed at end of DXE and
  308. marks all PRM_RUNTIME_MMIO_RANGE entries as EFI_MEMORY_RUNTIME.
  309. @param[in] Event Event whose notification function is being invoked.
  310. @param[in] Context The pointer to the notification function's context,
  311. which is implementation-dependent.
  312. **/
  313. VOID
  314. EFIAPI
  315. PrmConfigEndOfDxeNotification (
  316. IN EFI_EVENT Event,
  317. IN VOID *Context
  318. )
  319. {
  320. EFI_STATUS Status;
  321. UINTN HandleCount;
  322. UINTN BufferIndex;
  323. UINTN HandleIndex;
  324. EFI_HANDLE *HandleBuffer;
  325. PRM_CONTEXT_BUFFER *CurrentContextBuffer;
  326. PRM_CONFIG_PROTOCOL *PrmConfigProtocol;
  327. DEBUG ((DEBUG_INFO, "%a %a - Entry.\n", _DBGMSGID_, __FUNCTION__));
  328. HandleBuffer = NULL;
  329. Status = gBS->LocateHandleBuffer (
  330. ByProtocol,
  331. &gPrmConfigProtocolGuid,
  332. NULL,
  333. &HandleCount,
  334. &HandleBuffer
  335. );
  336. if (!EFI_ERROR (Status)) {
  337. for (HandleIndex = 0; HandleIndex < HandleCount; HandleIndex++) {
  338. Status = gBS->HandleProtocol (
  339. HandleBuffer[HandleIndex],
  340. &gPrmConfigProtocolGuid,
  341. (VOID **)&PrmConfigProtocol
  342. );
  343. ASSERT_EFI_ERROR (Status);
  344. if (EFI_ERROR (Status) || (PrmConfigProtocol == NULL)) {
  345. continue;
  346. }
  347. DEBUG ((
  348. DEBUG_INFO,
  349. " %a %a: Found PRM configuration protocol for PRM module %g.\n",
  350. _DBGMSGID_,
  351. __FUNCTION__,
  352. &PrmConfigProtocol->ModuleContextBuffers.ModuleGuid
  353. ));
  354. DEBUG ((DEBUG_INFO, " %a %a: Validating module context buffers...\n", _DBGMSGID_, __FUNCTION__));
  355. for (BufferIndex = 0; BufferIndex < PrmConfigProtocol->ModuleContextBuffers.BufferCount; BufferIndex++) {
  356. CurrentContextBuffer = &(PrmConfigProtocol->ModuleContextBuffers.Buffer[BufferIndex]);
  357. Status = ValidatePrmContextBuffer (CurrentContextBuffer);
  358. if (EFI_ERROR (Status)) {
  359. DEBUG ((
  360. DEBUG_ERROR,
  361. " %a %a: Context buffer validation failed for PRM handler %g.\n",
  362. _DBGMSGID_,
  363. __FUNCTION__,
  364. CurrentContextBuffer->HandlerGuid
  365. ));
  366. }
  367. }
  368. DEBUG ((DEBUG_INFO, " %a %a: Module context buffer validation complete.\n", _DBGMSGID_, __FUNCTION__));
  369. if (PrmConfigProtocol->ModuleContextBuffers.RuntimeMmioRanges != NULL) {
  370. DEBUG ((
  371. DEBUG_INFO,
  372. " %a %a: Found %d PRM runtime MMIO ranges.\n",
  373. _DBGMSGID_,
  374. __FUNCTION__,
  375. PrmConfigProtocol->ModuleContextBuffers.RuntimeMmioRanges->Count
  376. ));
  377. SetRuntimeMemoryRangeAttributes (PrmConfigProtocol->ModuleContextBuffers.RuntimeMmioRanges);
  378. mMaxRuntimeMmioRangeCount++;
  379. }
  380. }
  381. StoreVirtualMemoryAddressChangePointers ();
  382. }
  383. if (HandleBuffer != NULL) {
  384. gBS->FreePool (HandleBuffer);
  385. }
  386. gBS->CloseEvent (Event);
  387. }
  388. /**
  389. The entry point for this module.
  390. @param[in] ImageHandle The firmware allocated handle for the EFI image.
  391. @param[in] SystemTable A pointer to the EFI System Table.
  392. @retval EFI_SUCCESS The entry point is executed successfully.
  393. @retval Others An error occurred when executing this entry point.
  394. **/
  395. EFI_STATUS
  396. EFIAPI
  397. PrmConfigEntryPoint (
  398. IN EFI_HANDLE ImageHandle,
  399. IN EFI_SYSTEM_TABLE *SystemTable
  400. )
  401. {
  402. EFI_STATUS Status;
  403. EFI_EVENT Event;
  404. DEBUG ((DEBUG_INFO, "%a %a - Entry.\n", _DBGMSGID_, __FUNCTION__));
  405. //
  406. // Register a notification function to change memory attributes at end of DXE
  407. //
  408. Event = NULL;
  409. Status = gBS->CreateEventEx (
  410. EVT_NOTIFY_SIGNAL,
  411. TPL_CALLBACK,
  412. PrmConfigEndOfDxeNotification,
  413. NULL,
  414. &gEfiEndOfDxeEventGroupGuid,
  415. &Event
  416. );
  417. ASSERT_EFI_ERROR (Status);
  418. //
  419. // Register a notification function for virtual address change
  420. //
  421. Event = NULL;
  422. Status = gBS->CreateEventEx (
  423. EVT_NOTIFY_SIGNAL,
  424. TPL_NOTIFY,
  425. PrmConfigVirtualAddressChangeEvent,
  426. NULL,
  427. &gEfiEventVirtualAddressChangeGuid,
  428. &Event
  429. );
  430. ASSERT_EFI_ERROR (Status);
  431. return EFI_SUCCESS;
  432. }