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