SmiFeatures.c 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329
  1. /**@file
  2. Negotiate SMI features with QEMU, and configure UefiCpuPkg/PiSmmCpuDxeSmm
  3. accordingly.
  4. Copyright (C) 2016-2017, Red Hat, Inc.
  5. SPDX-License-Identifier: BSD-2-Clause-Patent
  6. **/
  7. #include <Library/BaseLib.h>
  8. #include <Library/DebugLib.h>
  9. #include <Library/MemEncryptSevLib.h>
  10. #include <Library/MemoryAllocationLib.h>
  11. #include <Library/PcdLib.h>
  12. #include <Library/QemuFwCfgLib.h>
  13. #include <Library/QemuFwCfgS3Lib.h>
  14. #include "SmiFeatures.h"
  15. //
  16. // The following bit value stands for "broadcast SMI" in the
  17. // "etc/smi/supported-features" and "etc/smi/requested-features" fw_cfg files.
  18. //
  19. #define ICH9_LPC_SMI_F_BROADCAST BIT0
  20. //
  21. // The following bit value stands for "enable CPU hotplug, and inject an SMI
  22. // with control value ICH9_APM_CNT_CPU_HOTPLUG upon hotplug", in the
  23. // "etc/smi/supported-features" and "etc/smi/requested-features" fw_cfg files.
  24. //
  25. #define ICH9_LPC_SMI_F_CPU_HOTPLUG BIT1
  26. //
  27. // The following bit value stands for "enable CPU hot-unplug, and inject an SMI
  28. // with control value ICH9_APM_CNT_CPU_HOTPLUG upon hot-unplug", in the
  29. // "etc/smi/supported-features" and "etc/smi/requested-features" fw_cfg files.
  30. //
  31. #define ICH9_LPC_SMI_F_CPU_HOT_UNPLUG BIT2
  32. //
  33. // Provides a scratch buffer (allocated in EfiReservedMemoryType type memory)
  34. // for the S3 boot script fragment to write to and read from.
  35. //
  36. #pragma pack (1)
  37. typedef union {
  38. UINT64 Features;
  39. UINT8 FeaturesOk;
  40. } SCRATCH_BUFFER;
  41. #pragma pack ()
  42. //
  43. // These carry the selector keys of the "etc/smi/requested-features" and
  44. // "etc/smi/features-ok" fw_cfg files from NegotiateSmiFeatures() to
  45. // AppendFwCfgBootScript().
  46. //
  47. STATIC FIRMWARE_CONFIG_ITEM mRequestedFeaturesItem;
  48. STATIC FIRMWARE_CONFIG_ITEM mFeaturesOkItem;
  49. //
  50. // Carries the negotiated SMI features from NegotiateSmiFeatures() to
  51. // AppendFwCfgBootScript().
  52. //
  53. STATIC UINT64 mSmiFeatures;
  54. /**
  55. Negotiate SMI features with QEMU.
  56. @retval FALSE If SMI feature negotiation is not supported by QEMU. This is
  57. not an error, it just means that SaveSmiFeatures() should not
  58. be called.
  59. @retval TRUE SMI feature negotiation is supported, and it has completed
  60. successfully as well. (Failure to negotiate is a fatal error
  61. and the function never returns in that case.)
  62. **/
  63. BOOLEAN
  64. NegotiateSmiFeatures (
  65. VOID
  66. )
  67. {
  68. FIRMWARE_CONFIG_ITEM SupportedFeaturesItem;
  69. UINTN SupportedFeaturesSize;
  70. UINTN RequestedFeaturesSize;
  71. UINTN FeaturesOkSize;
  72. UINT64 RequestedFeaturesMask;
  73. //
  74. // Look up the fw_cfg files used for feature negotiation. The selector keys
  75. // of "etc/smi/requested-features" and "etc/smi/features-ok" are saved
  76. // statically. If the files are missing, then QEMU doesn't support SMI
  77. // feature negotiation.
  78. //
  79. if (RETURN_ERROR (
  80. QemuFwCfgFindFile (
  81. "etc/smi/supported-features",
  82. &SupportedFeaturesItem,
  83. &SupportedFeaturesSize
  84. )
  85. ) ||
  86. RETURN_ERROR (
  87. QemuFwCfgFindFile (
  88. "etc/smi/requested-features",
  89. &mRequestedFeaturesItem,
  90. &RequestedFeaturesSize
  91. )
  92. ) ||
  93. RETURN_ERROR (
  94. QemuFwCfgFindFile (
  95. "etc/smi/features-ok",
  96. &mFeaturesOkItem,
  97. &FeaturesOkSize
  98. )
  99. ))
  100. {
  101. DEBUG ((
  102. DEBUG_INFO,
  103. "%a: SMI feature negotiation unavailable\n",
  104. __FUNCTION__
  105. ));
  106. return FALSE;
  107. }
  108. //
  109. // If the files are present but their sizes disagree with us, that's a fatal
  110. // error (we can't trust the behavior of SMIs either way).
  111. //
  112. if ((SupportedFeaturesSize != sizeof mSmiFeatures) ||
  113. (RequestedFeaturesSize != sizeof mSmiFeatures) ||
  114. (FeaturesOkSize != sizeof (UINT8)))
  115. {
  116. DEBUG ((
  117. DEBUG_ERROR,
  118. "%a: size mismatch in feature negotiation\n",
  119. __FUNCTION__
  120. ));
  121. goto FatalError;
  122. }
  123. //
  124. // Get the features supported by the host.
  125. //
  126. QemuFwCfgSelectItem (SupportedFeaturesItem);
  127. QemuFwCfgReadBytes (sizeof mSmiFeatures, &mSmiFeatures);
  128. //
  129. // We want broadcast SMI, SMI on CPU hotplug, SMI on CPU hot-unplug
  130. // and nothing else.
  131. //
  132. RequestedFeaturesMask = ICH9_LPC_SMI_F_BROADCAST;
  133. if (!MemEncryptSevIsEnabled ()) {
  134. //
  135. // For now, we only support hotplug with SEV disabled.
  136. //
  137. RequestedFeaturesMask |= ICH9_LPC_SMI_F_CPU_HOTPLUG;
  138. RequestedFeaturesMask |= ICH9_LPC_SMI_F_CPU_HOT_UNPLUG;
  139. }
  140. mSmiFeatures &= RequestedFeaturesMask;
  141. QemuFwCfgSelectItem (mRequestedFeaturesItem);
  142. QemuFwCfgWriteBytes (sizeof mSmiFeatures, &mSmiFeatures);
  143. //
  144. // Invoke feature validation in QEMU. If the selection is accepted, the
  145. // features will be locked down. If the selection is rejected, feature
  146. // negotiation remains open; however we don't know what to do in that case,
  147. // so that's a fatal error.
  148. //
  149. QemuFwCfgSelectItem (mFeaturesOkItem);
  150. if (QemuFwCfgRead8 () != 1) {
  151. DEBUG ((
  152. DEBUG_ERROR,
  153. "%a: negotiation failed for feature bitmap 0x%Lx\n",
  154. __FUNCTION__,
  155. mSmiFeatures
  156. ));
  157. goto FatalError;
  158. }
  159. if ((mSmiFeatures & ICH9_LPC_SMI_F_BROADCAST) == 0) {
  160. //
  161. // If we can't get broadcast SMIs from QEMU, that's acceptable too,
  162. // although not optimal.
  163. //
  164. DEBUG ((DEBUG_INFO, "%a: SMI broadcast unavailable\n", __FUNCTION__));
  165. } else {
  166. //
  167. // Configure the traditional AP sync / SMI delivery mode for
  168. // PiSmmCpuDxeSmm. Effectively, restore the UefiCpuPkg defaults, from which
  169. // the original QEMU behavior (i.e., unicast SMI) used to differ.
  170. //
  171. if (RETURN_ERROR (PcdSet64S (PcdCpuSmmApSyncTimeout, 1000000)) ||
  172. RETURN_ERROR (PcdSet8S (PcdCpuSmmSyncMode, 0x00)))
  173. {
  174. DEBUG ((
  175. DEBUG_ERROR,
  176. "%a: PiSmmCpuDxeSmm PCD configuration failed\n",
  177. __FUNCTION__
  178. ));
  179. goto FatalError;
  180. }
  181. DEBUG ((DEBUG_INFO, "%a: using SMI broadcast\n", __FUNCTION__));
  182. }
  183. if ((mSmiFeatures & ICH9_LPC_SMI_F_CPU_HOTPLUG) == 0) {
  184. DEBUG ((DEBUG_INFO, "%a: CPU hotplug not negotiated\n", __FUNCTION__));
  185. } else {
  186. DEBUG ((
  187. DEBUG_INFO,
  188. "%a: CPU hotplug with SMI negotiated\n",
  189. __FUNCTION__
  190. ));
  191. }
  192. if ((mSmiFeatures & ICH9_LPC_SMI_F_CPU_HOT_UNPLUG) == 0) {
  193. DEBUG ((DEBUG_INFO, "%a: CPU hot-unplug not negotiated\n", __FUNCTION__));
  194. } else {
  195. DEBUG ((
  196. DEBUG_INFO,
  197. "%a: CPU hot-unplug with SMI negotiated\n",
  198. __FUNCTION__
  199. ));
  200. }
  201. //
  202. // Negotiation successful (although we may not have gotten the optimal
  203. // feature set).
  204. //
  205. return TRUE;
  206. FatalError:
  207. ASSERT (FALSE);
  208. CpuDeadLoop ();
  209. //
  210. // Keep the compiler happy.
  211. //
  212. return FALSE;
  213. }
  214. /**
  215. FW_CFG_BOOT_SCRIPT_CALLBACK_FUNCTION provided to QemuFwCfgS3Lib.
  216. **/
  217. STATIC
  218. VOID
  219. EFIAPI
  220. AppendFwCfgBootScript (
  221. IN OUT VOID *Context OPTIONAL,
  222. IN OUT VOID *ExternalScratchBuffer
  223. )
  224. {
  225. SCRATCH_BUFFER *ScratchBuffer;
  226. RETURN_STATUS Status;
  227. ScratchBuffer = ExternalScratchBuffer;
  228. //
  229. // Write the negotiated feature bitmap into "etc/smi/requested-features".
  230. //
  231. ScratchBuffer->Features = mSmiFeatures;
  232. Status = QemuFwCfgS3ScriptWriteBytes (
  233. mRequestedFeaturesItem,
  234. sizeof ScratchBuffer->Features
  235. );
  236. if (RETURN_ERROR (Status)) {
  237. goto FatalError;
  238. }
  239. //
  240. // Read back "etc/smi/features-ok". This invokes the feature validation &
  241. // lockdown. (The validation succeeded at first boot.)
  242. //
  243. Status = QemuFwCfgS3ScriptReadBytes (
  244. mFeaturesOkItem,
  245. sizeof ScratchBuffer->FeaturesOk
  246. );
  247. if (RETURN_ERROR (Status)) {
  248. goto FatalError;
  249. }
  250. //
  251. // If "etc/smi/features-ok" read as 1, we're good. Otherwise, hang the S3
  252. // resume process.
  253. //
  254. Status = QemuFwCfgS3ScriptCheckValue (
  255. &ScratchBuffer->FeaturesOk,
  256. sizeof ScratchBuffer->FeaturesOk,
  257. MAX_UINT8,
  258. 1
  259. );
  260. if (RETURN_ERROR (Status)) {
  261. goto FatalError;
  262. }
  263. DEBUG ((
  264. DEBUG_VERBOSE,
  265. "%a: SMI feature negotiation boot script saved\n",
  266. __FUNCTION__
  267. ));
  268. return;
  269. FatalError:
  270. ASSERT (FALSE);
  271. CpuDeadLoop ();
  272. }
  273. /**
  274. Append a boot script fragment that will re-select the previously negotiated
  275. SMI features during S3 resume.
  276. **/
  277. VOID
  278. SaveSmiFeatures (
  279. VOID
  280. )
  281. {
  282. RETURN_STATUS Status;
  283. //
  284. // We are already running at TPL_CALLBACK, on the stack of
  285. // OnS3SaveStateInstalled(). But that's okay, we can easily queue more
  286. // notification functions while executing a notification function.
  287. //
  288. Status = QemuFwCfgS3CallWhenBootScriptReady (
  289. AppendFwCfgBootScript,
  290. NULL,
  291. sizeof (SCRATCH_BUFFER)
  292. );
  293. if (RETURN_ERROR (Status)) {
  294. ASSERT (FALSE);
  295. CpuDeadLoop ();
  296. }
  297. }