1 /** @file
2 
3   A PEIM with the following responsibilities:
4 
5   - verify & configure the Q35 TSEG in the entry point,
6   - provide SMRAM access by producing PEI_SMM_ACCESS_PPI,
7   - set aside the SMM_S3_RESUME_STATE object at the bottom of TSEG, and expose
8     it via the gEfiAcpiVariableGuid GUID HOB.
9 
10   This PEIM runs from RAM, so we can write to variables with static storage
11   duration.
12 
13   Copyright (C) 2013, 2015, Red Hat, Inc.<BR>
14   Copyright (c) 2010, Intel Corporation. All rights reserved.<BR>
15 
16   SPDX-License-Identifier: BSD-2-Clause-Patent
17 
18 **/
19 
20 #include <Guid/AcpiS3Context.h>
21 #include <Library/BaseLib.h>
22 #include <Library/BaseMemoryLib.h>
23 #include <Library/DebugLib.h>
24 #include <Library/HobLib.h>
25 #include <Library/IoLib.h>
26 #include <Library/PcdLib.h>
27 #include <Library/PciLib.h>
28 #include <Library/PeiServicesLib.h>
29 #include <Ppi/SmmAccess.h>
30 
31 #include <OvmfPlatforms.h>
32 
33 #include "SmramInternal.h"
34 
35 //
36 // PEI_SMM_ACCESS_PPI implementation.
37 //
38 
39 /**
40   Opens the SMRAM area to be accessible by a PEIM driver.
41 
42   This function "opens" SMRAM so that it is visible while not inside of SMM.
43   The function should return EFI_UNSUPPORTED if the hardware does not support
44   hiding of SMRAM. The function should return EFI_DEVICE_ERROR if the SMRAM
45   configuration is locked.
46 
47   @param  PeiServices            General purpose services available to every
48                                  PEIM.
49   @param  This                   The pointer to the SMM Access Interface.
50   @param  DescriptorIndex        The region of SMRAM to Open.
51 
52   @retval EFI_SUCCESS            The region was successfully opened.
53   @retval EFI_DEVICE_ERROR       The region could not be opened because locked
54                                  by chipset.
55   @retval EFI_INVALID_PARAMETER  The descriptor index was out of bounds.
56 
57 **/
58 STATIC
59 EFI_STATUS
60 EFIAPI
SmmAccessPeiOpen(IN EFI_PEI_SERVICES ** PeiServices,IN PEI_SMM_ACCESS_PPI * This,IN UINTN DescriptorIndex)61 SmmAccessPeiOpen (
62   IN EFI_PEI_SERVICES                **PeiServices,
63   IN PEI_SMM_ACCESS_PPI              *This,
64   IN UINTN                           DescriptorIndex
65   )
66 {
67   if (DescriptorIndex >= DescIdxCount) {
68     return EFI_INVALID_PARAMETER;
69   }
70 
71   //
72   // According to current practice, DescriptorIndex is not considered at all,
73   // beyond validating it.
74   //
75   return SmramAccessOpen (&This->LockState, &This->OpenState);
76 }
77 
78 /**
79   Inhibits access to the SMRAM.
80 
81   This function "closes" SMRAM so that it is not visible while outside of SMM.
82   The function should return EFI_UNSUPPORTED if the hardware does not support
83   hiding of SMRAM.
84 
85   @param  PeiServices              General purpose services available to every
86                                    PEIM.
87   @param  This                     The pointer to the SMM Access Interface.
88   @param  DescriptorIndex          The region of SMRAM to Close.
89 
90   @retval EFI_SUCCESS              The region was successfully closed.
91   @retval EFI_DEVICE_ERROR         The region could not be closed because
92                                    locked by chipset.
93   @retval EFI_INVALID_PARAMETER    The descriptor index was out of bounds.
94 
95 **/
96 STATIC
97 EFI_STATUS
98 EFIAPI
SmmAccessPeiClose(IN EFI_PEI_SERVICES ** PeiServices,IN PEI_SMM_ACCESS_PPI * This,IN UINTN DescriptorIndex)99 SmmAccessPeiClose (
100   IN EFI_PEI_SERVICES                **PeiServices,
101   IN PEI_SMM_ACCESS_PPI              *This,
102   IN UINTN                           DescriptorIndex
103   )
104 {
105   if (DescriptorIndex >= DescIdxCount) {
106     return EFI_INVALID_PARAMETER;
107   }
108 
109   //
110   // According to current practice, DescriptorIndex is not considered at all,
111   // beyond validating it.
112   //
113   return SmramAccessClose (&This->LockState, &This->OpenState);
114 }
115 
116 /**
117   Inhibits access to the SMRAM.
118 
119   This function prohibits access to the SMRAM region.  This function is usually
120   implemented such that it is a write-once operation.
121 
122   @param  PeiServices              General purpose services available to every
123                                    PEIM.
124   @param  This                     The pointer to the SMM Access Interface.
125   @param  DescriptorIndex          The region of SMRAM to Close.
126 
127   @retval EFI_SUCCESS            The region was successfully locked.
128   @retval EFI_DEVICE_ERROR       The region could not be locked because at
129                                  least one range is still open.
130   @retval EFI_INVALID_PARAMETER  The descriptor index was out of bounds.
131 
132 **/
133 STATIC
134 EFI_STATUS
135 EFIAPI
SmmAccessPeiLock(IN EFI_PEI_SERVICES ** PeiServices,IN PEI_SMM_ACCESS_PPI * This,IN UINTN DescriptorIndex)136 SmmAccessPeiLock (
137   IN EFI_PEI_SERVICES                **PeiServices,
138   IN PEI_SMM_ACCESS_PPI              *This,
139   IN UINTN                           DescriptorIndex
140   )
141 {
142   if (DescriptorIndex >= DescIdxCount) {
143     return EFI_INVALID_PARAMETER;
144   }
145 
146   //
147   // According to current practice, DescriptorIndex is not considered at all,
148   // beyond validating it.
149   //
150   return SmramAccessLock (&This->LockState, &This->OpenState);
151 }
152 
153 /**
154   Queries the memory controller for the possible regions that will support
155   SMRAM.
156 
157   @param  PeiServices           General purpose services available to every
158                                 PEIM.
159   @param This                   The pointer to the SmmAccessPpi Interface.
160   @param SmramMapSize           The pointer to the variable containing size of
161                                 the buffer to contain the description
162                                 information.
163   @param SmramMap               The buffer containing the data describing the
164                                 Smram region descriptors.
165 
166   @retval EFI_BUFFER_TOO_SMALL  The user did not provide a sufficient buffer.
167   @retval EFI_SUCCESS           The user provided a sufficiently-sized buffer.
168 
169 **/
170 STATIC
171 EFI_STATUS
172 EFIAPI
SmmAccessPeiGetCapabilities(IN EFI_PEI_SERVICES ** PeiServices,IN PEI_SMM_ACCESS_PPI * This,IN OUT UINTN * SmramMapSize,IN OUT EFI_SMRAM_DESCRIPTOR * SmramMap)173 SmmAccessPeiGetCapabilities (
174   IN EFI_PEI_SERVICES                **PeiServices,
175   IN PEI_SMM_ACCESS_PPI              *This,
176   IN OUT UINTN                       *SmramMapSize,
177   IN OUT EFI_SMRAM_DESCRIPTOR        *SmramMap
178   )
179 {
180   return SmramAccessGetCapabilities (This->LockState, This->OpenState,
181            SmramMapSize, SmramMap);
182 }
183 
184 //
185 // LockState and OpenState will be filled in by the entry point.
186 //
187 STATIC PEI_SMM_ACCESS_PPI mAccess = {
188   &SmmAccessPeiOpen,
189   &SmmAccessPeiClose,
190   &SmmAccessPeiLock,
191   &SmmAccessPeiGetCapabilities
192 };
193 
194 
195 STATIC EFI_PEI_PPI_DESCRIPTOR mPpiList[] = {
196   {
197     EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST,
198     &gPeiSmmAccessPpiGuid, &mAccess
199   }
200 };
201 
202 
203 //
204 // Utility functions.
205 //
206 STATIC
207 UINT8
CmosRead8(IN UINT8 Index)208 CmosRead8 (
209   IN UINT8 Index
210   )
211 {
212   IoWrite8 (0x70, Index);
213   return IoRead8 (0x71);
214 }
215 
216 STATIC
217 UINT32
GetSystemMemorySizeBelow4gb(VOID)218 GetSystemMemorySizeBelow4gb (
219   VOID
220   )
221 {
222   UINT32 Cmos0x34;
223   UINT32 Cmos0x35;
224 
225   Cmos0x34 = CmosRead8 (0x34);
226   Cmos0x35 = CmosRead8 (0x35);
227 
228   return ((Cmos0x35 << 8 | Cmos0x34) << 16) + SIZE_16MB;
229 }
230 
231 
232 //
233 // Entry point of this driver.
234 //
235 EFI_STATUS
236 EFIAPI
SmmAccessPeiEntryPoint(IN EFI_PEI_FILE_HANDLE FileHandle,IN CONST EFI_PEI_SERVICES ** PeiServices)237 SmmAccessPeiEntryPoint (
238   IN       EFI_PEI_FILE_HANDLE  FileHandle,
239   IN CONST EFI_PEI_SERVICES     **PeiServices
240   )
241 {
242   UINT16               HostBridgeDevId;
243   UINT8                EsmramcVal;
244   UINT8                RegMask8;
245   UINT32               TopOfLowRam, TopOfLowRamMb;
246   EFI_STATUS           Status;
247   UINTN                SmramMapSize;
248   EFI_SMRAM_DESCRIPTOR SmramMap[DescIdxCount];
249   VOID                 *GuidHob;
250 
251   //
252   // This module should only be included if SMRAM support is required.
253   //
254   ASSERT (FeaturePcdGet (PcdSmmSmramRequire));
255 
256   //
257   // Verify if we're running on a Q35 machine type.
258   //
259   HostBridgeDevId = PciRead16 (OVMF_HOSTBRIDGE_DID);
260   if (HostBridgeDevId != INTEL_Q35_MCH_DEVICE_ID) {
261     DEBUG ((EFI_D_ERROR, "%a: no SMRAM with host bridge DID=0x%04x; only "
262       "DID=0x%04x (Q35) is supported\n", __FUNCTION__, HostBridgeDevId,
263       INTEL_Q35_MCH_DEVICE_ID));
264     goto WrongConfig;
265   }
266 
267   //
268   // Confirm if QEMU supports SMRAM.
269   //
270   // With no support for it, the ESMRAMC (Extended System Management RAM
271   // Control) register reads as zero. If there is support, the cache-enable
272   // bits are hard-coded as 1 by QEMU.
273   //
274   EsmramcVal = PciRead8 (DRAMC_REGISTER_Q35 (MCH_ESMRAMC));
275   RegMask8 = MCH_ESMRAMC_SM_CACHE | MCH_ESMRAMC_SM_L1 | MCH_ESMRAMC_SM_L2;
276   if ((EsmramcVal & RegMask8) != RegMask8) {
277     DEBUG ((EFI_D_ERROR, "%a: this Q35 implementation lacks SMRAM\n",
278       __FUNCTION__));
279     goto WrongConfig;
280   }
281 
282   TopOfLowRam = GetSystemMemorySizeBelow4gb ();
283   ASSERT ((TopOfLowRam & (SIZE_1MB - 1)) == 0);
284   TopOfLowRamMb = TopOfLowRam >> 20;
285 
286   //
287   // Some of the following registers are no-ops for QEMU at the moment, but it
288   // is recommended to set them correctly, since the ESMRAMC that we ultimately
289   // care about is in the same set of registers.
290   //
291   // First, we disable the integrated VGA, and set both the GTT Graphics Memory
292   // Size and the Graphics Mode Select memory pre-allocation fields to zero.
293   // This takes just one write to the Graphics Control Register.
294   //
295   PciWrite16 (DRAMC_REGISTER_Q35 (MCH_GGC), MCH_GGC_IVD);
296 
297   //
298   // Set Top of Low Usable DRAM.
299   //
300   PciWrite16 (DRAMC_REGISTER_Q35 (MCH_TOLUD),
301     (UINT16)(TopOfLowRamMb << MCH_TOLUD_MB_SHIFT));
302 
303   //
304   // Given the zero graphics memory sizes configured above, set the
305   // graphics-related stolen memory bases to the same as TOLUD.
306   //
307   PciWrite32 (DRAMC_REGISTER_Q35 (MCH_GBSM),
308     TopOfLowRamMb << MCH_GBSM_MB_SHIFT);
309   PciWrite32 (DRAMC_REGISTER_Q35 (MCH_BGSM),
310     TopOfLowRamMb << MCH_BGSM_MB_SHIFT);
311 
312   //
313   // Set TSEG Memory Base.
314   //
315   InitQ35TsegMbytes ();
316   PciWrite32 (DRAMC_REGISTER_Q35 (MCH_TSEGMB),
317     (TopOfLowRamMb - mQ35TsegMbytes) << MCH_TSEGMB_MB_SHIFT);
318 
319   //
320   // Set TSEG size, and disable TSEG visibility outside of SMM. Note that the
321   // T_EN bit has inverse meaning; when T_EN is set, then TSEG visibility is
322   // *restricted* to SMM.
323   //
324   EsmramcVal &= ~(UINT32)MCH_ESMRAMC_TSEG_MASK;
325   EsmramcVal |= mQ35TsegMbytes == 8 ? MCH_ESMRAMC_TSEG_8MB :
326                 mQ35TsegMbytes == 2 ? MCH_ESMRAMC_TSEG_2MB :
327                 mQ35TsegMbytes == 1 ? MCH_ESMRAMC_TSEG_1MB :
328                 MCH_ESMRAMC_TSEG_EXT;
329   EsmramcVal |= MCH_ESMRAMC_T_EN;
330   PciWrite8 (DRAMC_REGISTER_Q35 (MCH_ESMRAMC), EsmramcVal);
331 
332   //
333   // TSEG should be closed (see above), but unlocked, initially. Set G_SMRAME
334   // (Global SMRAM Enable) too, as both D_LCK and T_EN depend on it.
335   //
336   PciAndThenOr8 (DRAMC_REGISTER_Q35 (MCH_SMRAM),
337     (UINT8)((~(UINT32)MCH_SMRAM_D_LCK) & 0xff), MCH_SMRAM_G_SMRAME);
338 
339   //
340   // Create the GUID HOB and point it to the first SMRAM range.
341   //
342   GetStates (&mAccess.LockState, &mAccess.OpenState);
343   SmramMapSize = sizeof SmramMap;
344   Status = SmramAccessGetCapabilities (mAccess.LockState, mAccess.OpenState,
345              &SmramMapSize, SmramMap);
346   ASSERT_EFI_ERROR (Status);
347 
348   DEBUG_CODE_BEGIN ();
349   {
350     UINTN Count;
351     UINTN Idx;
352 
353     Count = SmramMapSize / sizeof SmramMap[0];
354     DEBUG ((EFI_D_VERBOSE, "%a: SMRAM map follows, %d entries\n", __FUNCTION__,
355       (INT32)Count));
356     DEBUG ((EFI_D_VERBOSE, "% 20a % 20a % 20a % 20a\n", "PhysicalStart(0x)",
357       "PhysicalSize(0x)", "CpuStart(0x)", "RegionState(0x)"));
358     for (Idx = 0; Idx < Count; ++Idx) {
359       DEBUG ((EFI_D_VERBOSE, "% 20Lx % 20Lx % 20Lx % 20Lx\n",
360         SmramMap[Idx].PhysicalStart, SmramMap[Idx].PhysicalSize,
361         SmramMap[Idx].CpuStart, SmramMap[Idx].RegionState));
362     }
363   }
364   DEBUG_CODE_END ();
365 
366   GuidHob = BuildGuidHob (&gEfiAcpiVariableGuid,
367     sizeof SmramMap[DescIdxSmmS3ResumeState]);
368   if (GuidHob == NULL) {
369     return EFI_OUT_OF_RESOURCES;
370   }
371 
372   CopyMem (GuidHob, &SmramMap[DescIdxSmmS3ResumeState],
373     sizeof SmramMap[DescIdxSmmS3ResumeState]);
374 
375   //
376   // We're done. The next step should succeed, but even if it fails, we can't
377   // roll back the above BuildGuidHob() allocation, because PEI doesn't support
378   // releasing memory.
379   //
380   return PeiServicesInstallPpi (mPpiList);
381 
382 WrongConfig:
383   //
384   // We really don't want to continue in this case.
385   //
386   ASSERT (FALSE);
387   CpuDeadLoop ();
388   return EFI_UNSUPPORTED;
389 }
390