SmmControl2Dxe.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410
  1. /** @file
  2. A DXE_RUNTIME_DRIVER providing synchronous SMI activations via the
  3. EFI_SMM_CONTROL2_PROTOCOL.
  4. We expect the PEI phase to have covered the following:
  5. - ensure that the underlying QEMU machine type be X58
  6. (responsible: OvmfPkg/SmmAccess/SmmAccessPei.inf)
  7. - ensure that the ACPI PM IO space be configured
  8. (responsible: OvmfPkg/PlatformPei/PlatformPei.inf)
  9. Our own entry point is responsible for confirming the SMI feature and for
  10. configuring it.
  11. Copyright (C) 2013, 2015, Red Hat, Inc.<BR>
  12. Copyright (c) 2009 - 2019, Intel Corporation. All rights reserved.<BR>
  13. SPDX-License-Identifier: BSD-2-Clause-Patent
  14. **/
  15. #include <Register/X58Ich10.h>
  16. #include <Library/BaseLib.h>
  17. #include <Library/DebugLib.h>
  18. #include <Library/IoLib.h>
  19. #include <Library/PcdLib.h>
  20. #include <Library/PciLib.h>
  21. #include <Library/UefiBootServicesTableLib.h>
  22. #include <Protocol/S3SaveState.h>
  23. #include <Protocol/SmmControl2.h>
  24. //
  25. // Forward declaration.
  26. //
  27. STATIC
  28. VOID
  29. EFIAPI
  30. OnS3SaveStateInstalled (
  31. IN EFI_EVENT Event,
  32. IN VOID *Context
  33. );
  34. //
  35. // The absolute IO port address of the SMI Control and Enable Register. It is
  36. // only used to carry information from the entry point function to the
  37. // S3SaveState protocol installation callback, strictly before the runtime
  38. // phase.
  39. //
  40. STATIC UINTN mSmiEnable;
  41. //
  42. // Event signaled when an S3SaveState protocol interface is installed.
  43. //
  44. STATIC EFI_EVENT mS3SaveStateInstalled;
  45. /**
  46. Clear the SMI status
  47. @retval EFI_SUCCESS The function completes successfully
  48. @retval EFI_DEVICE_ERROR Something error occurred
  49. **/
  50. EFI_STATUS
  51. EFIAPI
  52. SmmClear(
  53. VOID
  54. )
  55. {
  56. EFI_STATUS Status;
  57. UINT32 OutputData;
  58. UINT32 OutputPort;
  59. UINT32 PmBase;
  60. Status = EFI_SUCCESS;
  61. PmBase = ICH10_PMBASE_IO;
  62. OutputPort = PmBase + ICH10_PMBASE_OFS_SMI_STS;
  63. OutputData = ICH10_SMI_STS_APM;
  64. IoWrite32(
  65. (UINTN)OutputPort,
  66. (UINT32)(OutputData)
  67. );
  68. ///
  69. /// Set the EOS Bit
  70. ///
  71. OutputPort = PmBase + ICH10_PMBASE_OFS_SMI_EN;
  72. OutputData = IoRead32((UINTN)OutputPort);
  73. OutputData |= ICH10_SMI_EN_EOS;
  74. IoWrite32(
  75. (UINTN)OutputPort,
  76. (UINT32)(OutputData)
  77. );
  78. ///
  79. /// There is no need to read EOS back and check if it is set.
  80. /// This can lead to a reading of zero if an SMI occurs right after the SMI_EN port read
  81. /// but before the data is returned to the CPU.
  82. /// SMM Dispatcher should make sure that EOS is set after all SMI sources are processed.
  83. ///
  84. return Status;
  85. }
  86. /**
  87. Invokes SMI activation from either the preboot or runtime environment.
  88. This function generates an SMI.
  89. @param[in] This The EFI_SMM_CONTROL2_PROTOCOL instance.
  90. @param[in,out] CommandPort The value written to the command port.
  91. @param[in,out] DataPort The value written to the data port.
  92. @param[in] Periodic Optional mechanism to engender a periodic
  93. stream.
  94. @param[in] ActivationInterval Optional parameter to repeat at this
  95. period one time or, if the Periodic
  96. Boolean is set, periodically.
  97. @retval EFI_SUCCESS The SMI/PMI has been engendered.
  98. @retval EFI_DEVICE_ERROR The timing is unsupported.
  99. @retval EFI_INVALID_PARAMETER The activation period is unsupported.
  100. @retval EFI_INVALID_PARAMETER The last periodic activation has not been
  101. cleared.
  102. @retval EFI_NOT_STARTED The SMM base service has not been initialized.
  103. **/
  104. STATIC
  105. EFI_STATUS
  106. EFIAPI
  107. SmmControl2DxeTrigger (
  108. IN CONST EFI_SMM_CONTROL2_PROTOCOL *This,
  109. IN OUT UINT8 *CommandPort OPTIONAL,
  110. IN OUT UINT8 *DataPort OPTIONAL,
  111. IN BOOLEAN Periodic OPTIONAL,
  112. IN UINTN ActivationInterval OPTIONAL
  113. )
  114. {
  115. EFI_STATUS Status;
  116. //
  117. // No support for queued or periodic activation.
  118. //
  119. if (Periodic || ActivationInterval > 0) {
  120. return EFI_DEVICE_ERROR;
  121. }
  122. ///
  123. /// Clear any pending the APM SMI
  124. ///
  125. Status = SmmClear();
  126. //
  127. // The so-called "Advanced Power Management Status Port Register" is in fact
  128. // a generic data passing register, between the caller and the SMI
  129. // dispatcher. The ICH9 spec calls it "scratchpad register" -- calling it
  130. // "status" elsewhere seems quite the misnomer. Status registers usually
  131. // report about hardware status, while this register is fully governed by
  132. // software.
  133. //
  134. // Write to the status register first, as this won't trigger the SMI just
  135. // yet. Then write to the control register.
  136. //
  137. IoWrite8 (ICH10_APM_STS, DataPort == NULL ? 0 : *DataPort);
  138. IoWrite8 (ICH10_APM_CNT, CommandPort == NULL ? 0 : *CommandPort);
  139. return EFI_SUCCESS;
  140. }
  141. /**
  142. Clears any system state that was created in response to the Trigger() call.
  143. This function acknowledges and causes the deassertion of the SMI activation
  144. source.
  145. @param[in] This The EFI_SMM_CONTROL2_PROTOCOL instance.
  146. @param[in] Periodic Optional parameter to repeat at this period
  147. one time
  148. @retval EFI_SUCCESS The SMI/PMI has been engendered.
  149. @retval EFI_DEVICE_ERROR The source could not be cleared.
  150. @retval EFI_INVALID_PARAMETER The service did not support the Periodic input
  151. argument.
  152. **/
  153. STATIC
  154. EFI_STATUS
  155. EFIAPI
  156. SmmControl2DxeClear (
  157. IN CONST EFI_SMM_CONTROL2_PROTOCOL *This,
  158. IN BOOLEAN Periodic OPTIONAL
  159. )
  160. {
  161. EFI_STATUS Status;
  162. if (Periodic) {
  163. return EFI_INVALID_PARAMETER;
  164. }
  165. //
  166. // The PI spec v1.4 explains that Clear() is only supposed to clear software
  167. // status; it is not in fact responsible for deasserting the SMI. It gives
  168. // two reasons for this: (a) many boards clear the SMI automatically when
  169. // entering SMM, (b) if Clear() actually deasserted the SMI, then it could
  170. // incorrectly suppress an SMI that was asynchronously asserted between the
  171. // last return of the SMI handler and the call made to Clear().
  172. //
  173. // In fact QEMU automatically deasserts CPU_INTERRUPT_SMI in:
  174. // - x86_cpu_exec_interrupt() [target-i386/seg_helper.c], and
  175. // - kvm_arch_pre_run() [target-i386/kvm.c].
  176. //
  177. // So, nothing to do here.
  178. //
  179. Status = SmmClear();
  180. return EFI_SUCCESS;
  181. }
  182. STATIC EFI_SMM_CONTROL2_PROTOCOL mControl2 = {
  183. &SmmControl2DxeTrigger,
  184. &SmmControl2DxeClear,
  185. MAX_UINTN // MinimumTriggerPeriod -- we don't support periodic SMIs
  186. };
  187. //
  188. // Entry point of this driver.
  189. //
  190. EFI_STATUS
  191. EFIAPI
  192. SmmControl2DxeEntryPoint (
  193. IN EFI_HANDLE ImageHandle,
  194. IN EFI_SYSTEM_TABLE *SystemTable
  195. )
  196. {
  197. UINT32 PmBase;
  198. UINT32 SmiEnableVal;
  199. EFI_STATUS Status;
  200. //
  201. // This module should only be included if SMRAM support is required.
  202. //
  203. ASSERT (FeaturePcdGet (PcdSmmSmramRequire));
  204. //
  205. // Calculate the absolute IO port address of the SMI Control and Enable
  206. // Register. (As noted at the top, the PEI phase has left us with a working
  207. // ACPI PM IO space.)
  208. //
  209. PmBase = PciRead32 (POWER_MGMT_REGISTER_ICH10 (ICH10_PMBASE)) &
  210. ICH10_PMBASE_MASK;
  211. mSmiEnable = PmBase + ICH10_PMBASE_OFS_SMI_EN;
  212. //
  213. // If APMC_EN is pre-set in SMI_EN, that's QEMU's way to tell us that SMI
  214. // support is not available. (For example due to KVM lacking it.) Otherwise,
  215. // this bit is clear after each reset.
  216. //
  217. SmiEnableVal = IoRead32 (mSmiEnable);
  218. if ((SmiEnableVal & ICH10_SMI_EN_APMC_EN) != 0) {
  219. DEBUG ((EFI_D_ERROR, "%a: this X58 implementation lacks SMI\n",
  220. __FUNCTION__));
  221. }
  222. //
  223. // Otherwise, configure the board to inject an SMI when ICH10_APM_CNT is
  224. // written to. (See the Trigger() method above.)
  225. //
  226. SmiEnableVal |= ICH10_SMI_EN_APMC_EN | ICH10_SMI_EN_GBL_SMI_EN;
  227. IoWrite32 (mSmiEnable, SmiEnableVal);
  228. //
  229. // Prevent software from undoing the above (until platform reset).
  230. //
  231. PciOr16 (POWER_MGMT_REGISTER_ICH10 (ICH10_GEN_PMCON_1),
  232. ICH10_GEN_PMCON_1_SMI_LOCK);
  233. //
  234. // If we can clear GBL_SMI_EN now, that means QEMU's SMI support is not
  235. // appropriate.
  236. //
  237. IoWrite32 (mSmiEnable, SmiEnableVal & ~(UINT32)ICH10_SMI_EN_GBL_SMI_EN);
  238. if (IoRead32 (mSmiEnable) != SmiEnableVal) {
  239. DEBUG ((EFI_D_ERROR, "%a: failed to lock down GBL_SMI_EN\n",
  240. __FUNCTION__));
  241. goto FatalError;
  242. }
  243. VOID *Registration;
  244. //
  245. // On S3 resume the above register settings have to be repeated. Register a
  246. // protocol notify callback that, when boot script saving becomes
  247. // available, saves operations equivalent to the above to the boot script.
  248. //
  249. Status = gBS->CreateEvent (EVT_NOTIFY_SIGNAL, TPL_CALLBACK,
  250. OnS3SaveStateInstalled, NULL /* Context */,
  251. &mS3SaveStateInstalled);
  252. if (EFI_ERROR (Status)) {
  253. DEBUG ((EFI_D_ERROR, "%a: CreateEvent: %r\n", __FUNCTION__, Status));
  254. goto FatalError;
  255. }
  256. Status = gBS->RegisterProtocolNotify (&gEfiS3SaveStateProtocolGuid,
  257. mS3SaveStateInstalled, &Registration);
  258. if (EFI_ERROR (Status)) {
  259. DEBUG ((EFI_D_ERROR, "%a: RegisterProtocolNotify: %r\n", __FUNCTION__,
  260. Status));
  261. goto ReleaseEvent;
  262. }
  263. //
  264. // Kick the event right now -- maybe the boot script is already saveable.
  265. //
  266. Status = gBS->SignalEvent (mS3SaveStateInstalled);
  267. if (EFI_ERROR (Status)) {
  268. DEBUG ((EFI_D_ERROR, "%a: SignalEvent: %r\n", __FUNCTION__, Status));
  269. goto ReleaseEvent;
  270. }
  271. //
  272. // We have no pointers to convert to virtual addresses. The handle itself
  273. // doesn't matter, as protocol services are not accessible at runtime.
  274. //
  275. Status = gBS->InstallMultipleProtocolInterfaces (&ImageHandle,
  276. &gEfiSmmControl2ProtocolGuid, &mControl2,
  277. NULL);
  278. if (EFI_ERROR (Status)) {
  279. DEBUG ((EFI_D_ERROR, "%a: InstallMultipleProtocolInterfaces: %r\n",
  280. __FUNCTION__, Status));
  281. goto ReleaseEvent;
  282. }
  283. return EFI_SUCCESS;
  284. ReleaseEvent:
  285. if (mS3SaveStateInstalled != NULL) {
  286. gBS->CloseEvent (mS3SaveStateInstalled);
  287. }
  288. FatalError:
  289. //
  290. // We really don't want to continue in this case.
  291. //
  292. ASSERT (FALSE);
  293. CpuDeadLoop ();
  294. return EFI_UNSUPPORTED;
  295. }
  296. /**
  297. Notification callback for S3SaveState installation.
  298. @param[in] Event Event whose notification function is being invoked.
  299. @param[in] Context The pointer to the notification function's context, which
  300. is implementation-dependent.
  301. **/
  302. STATIC
  303. VOID
  304. EFIAPI
  305. OnS3SaveStateInstalled (
  306. IN EFI_EVENT Event,
  307. IN VOID *Context
  308. )
  309. {
  310. EFI_STATUS Status;
  311. EFI_S3_SAVE_STATE_PROTOCOL *S3SaveState;
  312. UINT32 SmiEnOrMask, SmiEnAndMask;
  313. UINT16 GenPmCon1OrMask, GenPmCon1AndMask;
  314. ASSERT (Event == mS3SaveStateInstalled);
  315. Status = gBS->LocateProtocol (&gEfiS3SaveStateProtocolGuid,
  316. NULL /* Registration */, (VOID **)&S3SaveState);
  317. if (EFI_ERROR (Status)) {
  318. return;
  319. }
  320. //
  321. // These operations were originally done, verified and explained in the entry
  322. // point function of the driver.
  323. //
  324. SmiEnOrMask = ICH10_SMI_EN_APMC_EN | ICH10_SMI_EN_GBL_SMI_EN;
  325. SmiEnAndMask = MAX_UINT32;
  326. Status = S3SaveState->Write (
  327. S3SaveState,
  328. EFI_BOOT_SCRIPT_IO_READ_WRITE_OPCODE,
  329. EfiBootScriptWidthUint32,
  330. (UINT64)mSmiEnable,
  331. &SmiEnOrMask,
  332. &SmiEnAndMask
  333. );
  334. if (EFI_ERROR (Status)) {
  335. DEBUG ((EFI_D_ERROR, "%a: EFI_BOOT_SCRIPT_IO_READ_WRITE_OPCODE: %r\n",
  336. __FUNCTION__, Status));
  337. ASSERT (FALSE);
  338. CpuDeadLoop ();
  339. }
  340. GenPmCon1OrMask = ICH10_GEN_PMCON_1_SMI_LOCK;
  341. GenPmCon1AndMask = MAX_UINT16;
  342. Status = S3SaveState->Write (
  343. S3SaveState,
  344. EFI_BOOT_SCRIPT_PCI_CONFIG_READ_WRITE_OPCODE,
  345. EfiBootScriptWidthUint16,
  346. (UINT64)POWER_MGMT_REGISTER_ICH10 (ICH10_GEN_PMCON_1),
  347. &GenPmCon1OrMask,
  348. &GenPmCon1AndMask
  349. );
  350. if (EFI_ERROR (Status)) {
  351. DEBUG ((EFI_D_ERROR,
  352. "%a: EFI_BOOT_SCRIPT_PCI_CONFIG_READ_WRITE_OPCODE: %r\n", __FUNCTION__,
  353. Status));
  354. ASSERT (FALSE);
  355. CpuDeadLoop ();
  356. }
  357. DEBUG ((EFI_D_VERBOSE, "%a: boot script fragment saved\n", __FUNCTION__));
  358. gBS->CloseEvent (Event);
  359. mS3SaveStateInstalled = NULL;
  360. }