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