1 /** @file
2
3 Functions and types shared by the SMM accessor PEI and DXE modules.
4
5 Copyright (C) 2015, Red Hat, Inc.
6
7 This program and the accompanying materials are licensed and made available
8 under the terms and conditions of the BSD License which accompanies this
9 distribution. The full text of the license may be found at
10 http://opensource.org/licenses/bsd-license.php
11
12 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, WITHOUT
13 WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
14
15 **/
16
17 #include <Guid/AcpiS3Context.h>
18 #include <IndustryStandard/Q35MchIch9.h>
19 #include <Library/DebugLib.h>
20 #include <Library/PciLib.h>
21
22 #include "SmramInternal.h"
23
24 /**
25 Read the MCH_SMRAM and ESMRAMC registers, and update the LockState and
26 OpenState fields in the PEI_SMM_ACCESS_PPI / EFI_SMM_ACCESS2_PROTOCOL object,
27 from the D_LCK and T_EN bits.
28
29 PEI_SMM_ACCESS_PPI and EFI_SMM_ACCESS2_PROTOCOL member functions can rely on
30 the LockState and OpenState fields being up-to-date on entry, and they need
31 to restore the same invariant on exit, if they touch the bits in question.
32
33 @param[out] LockState Reflects the D_LCK bit on output; TRUE iff SMRAM is
34 locked.
35 @param[out] OpenState Reflects the inverse of the T_EN bit on output; TRUE
36 iff SMRAM is open.
37 **/
38 VOID
GetStates(OUT BOOLEAN * LockState,OUT BOOLEAN * OpenState)39 GetStates (
40 OUT BOOLEAN *LockState,
41 OUT BOOLEAN *OpenState
42 )
43 {
44 UINT8 SmramVal, EsmramcVal;
45
46 SmramVal = PciRead8 (DRAMC_REGISTER_Q35 (MCH_SMRAM));
47 EsmramcVal = PciRead8 (DRAMC_REGISTER_Q35 (MCH_ESMRAMC));
48
49 *LockState = !!(SmramVal & MCH_SMRAM_D_LCK);
50 *OpenState = !(EsmramcVal & MCH_ESMRAMC_T_EN);
51 }
52
53 //
54 // The functions below follow the PEI_SMM_ACCESS_PPI and
55 // EFI_SMM_ACCESS2_PROTOCOL member declarations. The PeiServices and This
56 // pointers are removed (TSEG doesn't depend on them), and so is the
57 // DescriptorIndex parameter (TSEG doesn't support range-wise locking).
58 //
59 // The LockState and OpenState members that are common to both
60 // PEI_SMM_ACCESS_PPI and EFI_SMM_ACCESS2_PROTOCOL are taken and updated in
61 // isolation from the rest of the (non-shared) members.
62 //
63
64 EFI_STATUS
SmramAccessOpen(OUT BOOLEAN * LockState,OUT BOOLEAN * OpenState)65 SmramAccessOpen (
66 OUT BOOLEAN *LockState,
67 OUT BOOLEAN *OpenState
68 )
69 {
70 //
71 // Open TSEG by clearing T_EN.
72 //
73 PciAnd8 (DRAMC_REGISTER_Q35 (MCH_ESMRAMC),
74 (UINT8)((~(UINT32)MCH_ESMRAMC_T_EN) & 0xff));
75
76 GetStates (LockState, OpenState);
77 if (!*OpenState) {
78 return EFI_DEVICE_ERROR;
79 }
80 return EFI_SUCCESS;
81 }
82
83 EFI_STATUS
SmramAccessClose(OUT BOOLEAN * LockState,OUT BOOLEAN * OpenState)84 SmramAccessClose (
85 OUT BOOLEAN *LockState,
86 OUT BOOLEAN *OpenState
87 )
88 {
89 //
90 // Close TSEG by setting T_EN.
91 //
92 PciOr8 (DRAMC_REGISTER_Q35 (MCH_ESMRAMC), MCH_ESMRAMC_T_EN);
93
94 GetStates (LockState, OpenState);
95 if (*OpenState) {
96 return EFI_DEVICE_ERROR;
97 }
98 return EFI_SUCCESS;
99 }
100
101 EFI_STATUS
SmramAccessLock(OUT BOOLEAN * LockState,IN OUT BOOLEAN * OpenState)102 SmramAccessLock (
103 OUT BOOLEAN *LockState,
104 IN OUT BOOLEAN *OpenState
105 )
106 {
107 if (*OpenState) {
108 return EFI_DEVICE_ERROR;
109 }
110
111 //
112 // Close & lock TSEG by setting T_EN and D_LCK.
113 //
114 PciOr8 (DRAMC_REGISTER_Q35 (MCH_ESMRAMC), MCH_ESMRAMC_T_EN);
115 PciOr8 (DRAMC_REGISTER_Q35 (MCH_SMRAM), MCH_SMRAM_D_LCK);
116
117 GetStates (LockState, OpenState);
118 if (*OpenState || !*LockState) {
119 return EFI_DEVICE_ERROR;
120 }
121 return EFI_SUCCESS;
122 }
123
124 EFI_STATUS
SmramAccessGetCapabilities(IN BOOLEAN LockState,IN BOOLEAN OpenState,IN OUT UINTN * SmramMapSize,IN OUT EFI_SMRAM_DESCRIPTOR * SmramMap)125 SmramAccessGetCapabilities (
126 IN BOOLEAN LockState,
127 IN BOOLEAN OpenState,
128 IN OUT UINTN *SmramMapSize,
129 IN OUT EFI_SMRAM_DESCRIPTOR *SmramMap
130 )
131 {
132 UINTN OriginalSize;
133 UINT32 TsegMemoryBaseMb, TsegMemoryBase;
134 UINT64 CommonRegionState;
135 UINT8 TsegSizeBits;
136
137 OriginalSize = *SmramMapSize;
138 *SmramMapSize = DescIdxCount * sizeof *SmramMap;
139 if (OriginalSize < *SmramMapSize) {
140 return EFI_BUFFER_TOO_SMALL;
141 }
142
143 //
144 // Read the TSEG Memory Base register.
145 //
146 TsegMemoryBaseMb = PciRead32 (DRAMC_REGISTER_Q35 (MCH_TSEGMB));
147 TsegMemoryBase = (TsegMemoryBaseMb >> MCH_TSEGMB_MB_SHIFT) << 20;
148
149 //
150 // Precompute the region state bits that will be set for all regions.
151 //
152 CommonRegionState = (OpenState ? EFI_SMRAM_OPEN : EFI_SMRAM_CLOSED) |
153 (LockState ? EFI_SMRAM_LOCKED : 0) |
154 EFI_CACHEABLE;
155
156 //
157 // The first region hosts an SMM_S3_RESUME_STATE object. It is located at the
158 // start of TSEG. We round up the size to whole pages, and we report it as
159 // EFI_ALLOCATED, so that the SMM_CORE stays away from it.
160 //
161 SmramMap[DescIdxSmmS3ResumeState].PhysicalStart = TsegMemoryBase;
162 SmramMap[DescIdxSmmS3ResumeState].CpuStart = TsegMemoryBase;
163 SmramMap[DescIdxSmmS3ResumeState].PhysicalSize =
164 EFI_PAGES_TO_SIZE (EFI_SIZE_TO_PAGES (sizeof (SMM_S3_RESUME_STATE)));
165 SmramMap[DescIdxSmmS3ResumeState].RegionState =
166 CommonRegionState | EFI_ALLOCATED;
167
168 //
169 // Get the TSEG size bits from the ESMRAMC register.
170 //
171 TsegSizeBits = PciRead8 (DRAMC_REGISTER_Q35 (MCH_ESMRAMC)) &
172 MCH_ESMRAMC_TSEG_MASK;
173
174 //
175 // The second region is the main one, following the first.
176 //
177 SmramMap[DescIdxMain].PhysicalStart =
178 SmramMap[DescIdxSmmS3ResumeState].PhysicalStart +
179 SmramMap[DescIdxSmmS3ResumeState].PhysicalSize;
180 SmramMap[DescIdxMain].CpuStart = SmramMap[DescIdxMain].PhysicalStart;
181 SmramMap[DescIdxMain].PhysicalSize =
182 (TsegSizeBits == MCH_ESMRAMC_TSEG_8MB ? SIZE_8MB :
183 TsegSizeBits == MCH_ESMRAMC_TSEG_2MB ? SIZE_2MB :
184 SIZE_1MB) - SmramMap[DescIdxSmmS3ResumeState].PhysicalSize;
185 SmramMap[DescIdxMain].RegionState = CommonRegionState;
186
187 return EFI_SUCCESS;
188 }
189