1 | /** @file
|
---|
2 | Agent Module to load other modules to deploy SMM Entry Vector for X86 CPU.
|
---|
3 |
|
---|
4 | Copyright (c) 2009 - 2024, Intel Corporation. All rights reserved.<BR>
|
---|
5 | Copyright (c) 2017, AMD Incorporated. All rights reserved.<BR>
|
---|
6 | Copyright (C) 2023 - 2024 Advanced Micro Devices, Inc. All rights reserved.<BR>
|
---|
7 |
|
---|
8 | SPDX-License-Identifier: BSD-2-Clause-Patent
|
---|
9 |
|
---|
10 | **/
|
---|
11 |
|
---|
12 | #include "PiSmmCpuCommon.h"
|
---|
13 | #include <Library/UefiBootServicesTableLib.h>
|
---|
14 |
|
---|
15 | //
|
---|
16 | // TRUE to indicate it's the MM_STANDALONE MM CPU driver.
|
---|
17 | // FALSE to indicate it's the DXE_SMM_DRIVER SMM CPU driver.
|
---|
18 | //
|
---|
19 | const BOOLEAN mIsStandaloneMm = FALSE;
|
---|
20 |
|
---|
21 | //
|
---|
22 | // SMM ready to lock flag
|
---|
23 | //
|
---|
24 | BOOLEAN mSmmReadyToLock = FALSE;
|
---|
25 |
|
---|
26 | /**
|
---|
27 | Check SmmProfile is enabled or not.
|
---|
28 |
|
---|
29 | @return TRUE SmmProfile is enabled.
|
---|
30 | FALSE SmmProfile is not enabled.
|
---|
31 |
|
---|
32 | **/
|
---|
33 | BOOLEAN
|
---|
34 | IsSmmProfileEnabled (
|
---|
35 | VOID
|
---|
36 | )
|
---|
37 | {
|
---|
38 | return FeaturePcdGet (PcdCpuSmmProfileEnable);
|
---|
39 | }
|
---|
40 |
|
---|
41 | /**
|
---|
42 | Perform the remaining tasks.
|
---|
43 |
|
---|
44 | **/
|
---|
45 | VOID
|
---|
46 | PerformRemainingTasks (
|
---|
47 | VOID
|
---|
48 | )
|
---|
49 | {
|
---|
50 | EDKII_PI_SMM_MEMORY_ATTRIBUTES_TABLE *MemoryAttributesTable;
|
---|
51 |
|
---|
52 | if (mSmmReadyToLock) {
|
---|
53 | PERF_FUNCTION_BEGIN ();
|
---|
54 |
|
---|
55 | //
|
---|
56 | // Start SMM Profile feature
|
---|
57 | //
|
---|
58 | if (mSmmProfileEnabled) {
|
---|
59 | SmmProfileStart ();
|
---|
60 | }
|
---|
61 |
|
---|
62 | //
|
---|
63 | // Check if all Aps enter SMM. In Relaxed-AP Sync Mode, BSP will not wait for
|
---|
64 | // all Aps arrive. However,PerformRemainingTasks() needs to wait all Aps arrive before calling
|
---|
65 | // SetMemMapAttributes() and ConfigSmmCodeAccessCheck() when mSmmReadyToLock
|
---|
66 | // is true. In SetMemMapAttributes(), SmmSetMemoryAttributesEx() will call
|
---|
67 | // FlushTlbForAll() that need to start up the aps. So it need to let all
|
---|
68 | // aps arrive. Same as SetMemMapAttributes(), ConfigSmmCodeAccessCheck()
|
---|
69 | // also will start up the aps.
|
---|
70 | //
|
---|
71 | if (EFI_ERROR (SmmCpuRendezvous (NULL, TRUE))) {
|
---|
72 | DEBUG ((DEBUG_ERROR, "PerformRemainingTasks: fail to wait for all AP check in SMM!\n"));
|
---|
73 | }
|
---|
74 |
|
---|
75 | //
|
---|
76 | // Update Page Table for outside SMRAM.
|
---|
77 | //
|
---|
78 | if (mSmmProfileEnabled) {
|
---|
79 | SmmProfileUpdateMemoryAttributes ();
|
---|
80 | } else {
|
---|
81 | UpdateUefiMemMapAttributes ();
|
---|
82 | }
|
---|
83 |
|
---|
84 | //
|
---|
85 | // gEdkiiPiSmmMemoryAttributesTableGuid should have been published at EndOfDxe by SmmCore
|
---|
86 | // Note: gEdkiiPiSmmMemoryAttributesTableGuid is not always installed since it depends on
|
---|
87 | // the memory protection attribute setting in MM Core.
|
---|
88 | //
|
---|
89 | SmmGetSystemConfigurationTable (&gEdkiiPiSmmMemoryAttributesTableGuid, (VOID **)&MemoryAttributesTable);
|
---|
90 |
|
---|
91 | //
|
---|
92 | // Set critical region attribute in page table according to the MemoryAttributesTable
|
---|
93 | //
|
---|
94 | if (MemoryAttributesTable != NULL) {
|
---|
95 | SetMemMapAttributes (MemoryAttributesTable);
|
---|
96 | }
|
---|
97 |
|
---|
98 | //
|
---|
99 | // Set page table itself to be read-only
|
---|
100 | //
|
---|
101 | SetPageTableAttributes ();
|
---|
102 |
|
---|
103 | //
|
---|
104 | // Configure SMM Code Access Check feature if available.
|
---|
105 | //
|
---|
106 | ConfigSmmCodeAccessCheck ();
|
---|
107 |
|
---|
108 | //
|
---|
109 | // Measure performance of SmmCpuFeaturesCompleteSmmReadyToLock() from caller side
|
---|
110 | // as the implementation is provided by platform.
|
---|
111 | //
|
---|
112 | PERF_START (NULL, "SmmCompleteReadyToLock", NULL, 0);
|
---|
113 | SmmCpuFeaturesCompleteSmmReadyToLock ();
|
---|
114 | PERF_END (NULL, "SmmCompleteReadyToLock", NULL, 0);
|
---|
115 |
|
---|
116 | //
|
---|
117 | // Clean SMM ready to lock flag
|
---|
118 | //
|
---|
119 | mSmmReadyToLock = FALSE;
|
---|
120 |
|
---|
121 | PERF_FUNCTION_END ();
|
---|
122 | }
|
---|
123 | }
|
---|
124 |
|
---|
125 | /**
|
---|
126 | To get system port address of the SMI Command Port in FADT table.
|
---|
127 |
|
---|
128 | **/
|
---|
129 | VOID
|
---|
130 | GetSmiCommandPort (
|
---|
131 | VOID
|
---|
132 | )
|
---|
133 | {
|
---|
134 | EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE *Fadt;
|
---|
135 |
|
---|
136 | Fadt = (EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE *)EfiLocateFirstAcpiTable (
|
---|
137 | EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE_SIGNATURE
|
---|
138 | );
|
---|
139 |
|
---|
140 | if (Fadt == NULL) {
|
---|
141 | ASSERT (Fadt != NULL);
|
---|
142 | return;
|
---|
143 | }
|
---|
144 |
|
---|
145 | mSmiCommandPort = Fadt->SmiCmd;
|
---|
146 | DEBUG ((DEBUG_INFO, "mSmiCommandPort = %x\n", mSmiCommandPort));
|
---|
147 | }
|
---|
148 |
|
---|
149 | /**
|
---|
150 | SMM Ready To Lock event notification handler.
|
---|
151 |
|
---|
152 | mSmmReadyToLock is set to perform additional lock actions that must be
|
---|
153 | performed from SMM on the next SMI.
|
---|
154 |
|
---|
155 | @param[in] Protocol Points to the protocol's unique identifier.
|
---|
156 | @param[in] Interface Points to the interface instance.
|
---|
157 | @param[in] Handle The handle on which the interface was installed.
|
---|
158 |
|
---|
159 | @retval EFI_SUCCESS Notification handler runs successfully.
|
---|
160 | **/
|
---|
161 | EFI_STATUS
|
---|
162 | EFIAPI
|
---|
163 | SmmReadyToLockEventNotify (
|
---|
164 | IN CONST EFI_GUID *Protocol,
|
---|
165 | IN VOID *Interface,
|
---|
166 | IN EFI_HANDLE Handle
|
---|
167 | )
|
---|
168 | {
|
---|
169 | //
|
---|
170 | // Cache a copy of UEFI memory map before we start profiling feature.
|
---|
171 | //
|
---|
172 | GetUefiMemoryMap ();
|
---|
173 |
|
---|
174 | //
|
---|
175 | // Skip SMM profile initialization if feature is disabled
|
---|
176 | //
|
---|
177 | if (mSmmProfileEnabled) {
|
---|
178 | //
|
---|
179 | // Get Software SMI from FADT
|
---|
180 | //
|
---|
181 | GetSmiCommandPort ();
|
---|
182 |
|
---|
183 | //
|
---|
184 | // Initialize protected memory range for patching page table later.
|
---|
185 | //
|
---|
186 | InitProtectedMemRange ();
|
---|
187 | }
|
---|
188 |
|
---|
189 | //
|
---|
190 | // Set SMM ready to lock flag and return
|
---|
191 | //
|
---|
192 | mSmmReadyToLock = TRUE;
|
---|
193 | return EFI_SUCCESS;
|
---|
194 | }
|
---|
195 |
|
---|
196 | /**
|
---|
197 | Get SmmCpuSyncConfig data: RelaxedMode, SyncTimeout, SyncTimeout2.
|
---|
198 |
|
---|
199 | @param[in,out] RelaxedMode It indicates if Relaxed CPU synchronization method or
|
---|
200 | traditional CPU synchronization method is used when processing an SMI.
|
---|
201 | @param[in,out] SyncTimeout It indicates the 1st BSP/AP synchronization timeout value in SMM.
|
---|
202 | @param[in,out] SyncTimeout2 It indicates the 2nd BSP/AP synchronization timeout value in SMM.
|
---|
203 |
|
---|
204 | **/
|
---|
205 | VOID
|
---|
206 | GetSmmCpuSyncConfigData (
|
---|
207 | IN OUT BOOLEAN *RelaxedMode, OPTIONAL
|
---|
208 | IN OUT UINT64 *SyncTimeout, OPTIONAL
|
---|
209 | IN OUT UINT64 *SyncTimeout2 OPTIONAL
|
---|
210 | )
|
---|
211 | {
|
---|
212 | if (RelaxedMode != NULL) {
|
---|
213 | *RelaxedMode = (BOOLEAN)(PcdGet8 (PcdCpuSmmSyncMode) == MmCpuSyncModeRelaxedAp);
|
---|
214 | }
|
---|
215 |
|
---|
216 | if (SyncTimeout != NULL) {
|
---|
217 | *SyncTimeout = PcdGet64 (PcdCpuSmmApSyncTimeout);
|
---|
218 | }
|
---|
219 |
|
---|
220 | if (SyncTimeout2 != NULL) {
|
---|
221 | *SyncTimeout2 = PcdGet64 (PcdCpuSmmApSyncTimeout2);
|
---|
222 | }
|
---|
223 | }
|
---|
224 |
|
---|
225 | /**
|
---|
226 | Get ACPI S3 enable flag.
|
---|
227 |
|
---|
228 | **/
|
---|
229 | VOID
|
---|
230 | GetAcpiS3EnableFlag (
|
---|
231 | VOID
|
---|
232 | )
|
---|
233 | {
|
---|
234 | mAcpiS3Enable = PcdGetBool (PcdAcpiS3Enable);
|
---|
235 | }
|
---|
236 |
|
---|
237 | /**
|
---|
238 | Get the maximum number of logical processors supported by the system.
|
---|
239 |
|
---|
240 | @retval The maximum number of logical processors supported by the system
|
---|
241 | is indicated by the return value.
|
---|
242 | **/
|
---|
243 | UINTN
|
---|
244 | GetSupportedMaxLogicalProcessorNumber (
|
---|
245 | VOID
|
---|
246 | )
|
---|
247 | {
|
---|
248 | return PcdGet32 (PcdCpuMaxLogicalProcessorNumber);
|
---|
249 | }
|
---|
250 |
|
---|
251 | /**
|
---|
252 | Extract NumberOfCpus, MaxNumberOfCpus and EFI_PROCESSOR_INFORMATION for all CPU from gEfiMpServiceProtocolGuid.
|
---|
253 |
|
---|
254 | @param[out] NumberOfCpus Pointer to NumberOfCpus.
|
---|
255 | @param[out] MaxNumberOfCpus Pointer to MaxNumberOfCpus.
|
---|
256 |
|
---|
257 | @retval ProcessorInfo Pointer to EFI_PROCESSOR_INFORMATION buffer.
|
---|
258 | **/
|
---|
259 | EFI_PROCESSOR_INFORMATION *
|
---|
260 | GetMpInformationFromMpServices (
|
---|
261 | OUT UINTN *NumberOfCpus,
|
---|
262 | OUT UINTN *MaxNumberOfCpus
|
---|
263 | )
|
---|
264 | {
|
---|
265 | EFI_STATUS Status;
|
---|
266 | UINTN Index;
|
---|
267 | UINTN NumberOfEnabledProcessors;
|
---|
268 | UINTN NumberOfProcessors;
|
---|
269 | EFI_MP_SERVICES_PROTOCOL *MpService;
|
---|
270 | EFI_PROCESSOR_INFORMATION *ProcessorInfo;
|
---|
271 |
|
---|
272 | if ((NumberOfCpus == NULL) || (MaxNumberOfCpus == NULL)) {
|
---|
273 | ASSERT_EFI_ERROR (EFI_INVALID_PARAMETER);
|
---|
274 | return NULL;
|
---|
275 | }
|
---|
276 |
|
---|
277 | ProcessorInfo = NULL;
|
---|
278 | *NumberOfCpus = 0;
|
---|
279 | *MaxNumberOfCpus = 0;
|
---|
280 |
|
---|
281 | /// Get the MP Services Protocol
|
---|
282 | Status = gBS->LocateProtocol (&gEfiMpServiceProtocolGuid, NULL, (VOID **)&MpService);
|
---|
283 | if (EFI_ERROR (Status)) {
|
---|
284 | ASSERT_EFI_ERROR (Status);
|
---|
285 | return NULL;
|
---|
286 | }
|
---|
287 |
|
---|
288 | /// Get the number of processors
|
---|
289 | Status = MpService->GetNumberOfProcessors (MpService, &NumberOfProcessors, &NumberOfEnabledProcessors);
|
---|
290 | if (EFI_ERROR (Status)) {
|
---|
291 | ASSERT_EFI_ERROR (Status);
|
---|
292 | return NULL;
|
---|
293 | }
|
---|
294 |
|
---|
295 | ASSERT (NumberOfProcessors <= GetSupportedMaxLogicalProcessorNumber ());
|
---|
296 |
|
---|
297 | /// Allocate buffer for processor information
|
---|
298 | ProcessorInfo = AllocateZeroPool (sizeof (EFI_PROCESSOR_INFORMATION) * NumberOfProcessors);
|
---|
299 | if (ProcessorInfo == NULL) {
|
---|
300 | ASSERT_EFI_ERROR (EFI_OUT_OF_RESOURCES);
|
---|
301 | return NULL;
|
---|
302 | }
|
---|
303 |
|
---|
304 | /// Get processor information
|
---|
305 | for (Index = 0; Index < NumberOfProcessors; Index++) {
|
---|
306 | Status = MpService->GetProcessorInfo (MpService, Index | CPU_V2_EXTENDED_TOPOLOGY, &ProcessorInfo[Index]);
|
---|
307 | if (EFI_ERROR (Status)) {
|
---|
308 | FreePool (ProcessorInfo);
|
---|
309 | DEBUG ((DEBUG_ERROR, "%a: Failed to get processor information for processor %d\n", __func__, Index));
|
---|
310 | ASSERT_EFI_ERROR (Status);
|
---|
311 | return NULL;
|
---|
312 | }
|
---|
313 | }
|
---|
314 |
|
---|
315 | *NumberOfCpus = NumberOfEnabledProcessors;
|
---|
316 |
|
---|
317 | ASSERT (*NumberOfCpus <= GetSupportedMaxLogicalProcessorNumber ());
|
---|
318 | //
|
---|
319 | // If support CPU hot plug, we need to allocate resources for possibly hot-added processors
|
---|
320 | //
|
---|
321 | if (FeaturePcdGet (PcdCpuHotPlugSupport)) {
|
---|
322 | *MaxNumberOfCpus = GetSupportedMaxLogicalProcessorNumber ();
|
---|
323 | } else {
|
---|
324 | *MaxNumberOfCpus = *NumberOfCpus;
|
---|
325 | }
|
---|
326 |
|
---|
327 | return ProcessorInfo;
|
---|
328 | }
|
---|
329 |
|
---|
330 | /**
|
---|
331 | The module Entry Point of the CPU SMM driver.
|
---|
332 |
|
---|
333 | @param ImageHandle The firmware allocated handle for the EFI image.
|
---|
334 | @param SystemTable A pointer to the EFI System Table.
|
---|
335 |
|
---|
336 | @retval EFI_SUCCESS The entry point is executed successfully.
|
---|
337 | @retval Other Some error occurs when executing this entry point.
|
---|
338 |
|
---|
339 | **/
|
---|
340 | EFI_STATUS
|
---|
341 | EFIAPI
|
---|
342 | PiCpuSmmEntry (
|
---|
343 | IN EFI_HANDLE ImageHandle,
|
---|
344 | IN EFI_SYSTEM_TABLE *SystemTable
|
---|
345 | )
|
---|
346 | {
|
---|
347 | EFI_STATUS Status;
|
---|
348 | VOID *Registration;
|
---|
349 |
|
---|
350 | //
|
---|
351 | // Save the PcdPteMemoryEncryptionAddressOrMask value into a global variable.
|
---|
352 | // Make sure AddressEncMask is contained to smallest supported address field.
|
---|
353 | //
|
---|
354 | mAddressEncMask = PcdGet64 (PcdPteMemoryEncryptionAddressOrMask) & PAGING_1G_ADDRESS_MASK_64;
|
---|
355 | DEBUG ((DEBUG_INFO, "mAddressEncMask = 0x%lx\n", mAddressEncMask));
|
---|
356 |
|
---|
357 | Status = PiSmmCpuEntryCommon ();
|
---|
358 |
|
---|
359 | ASSERT_EFI_ERROR (Status);
|
---|
360 |
|
---|
361 | //
|
---|
362 | // Install the SMM Configuration Protocol onto a new handle on the handle database.
|
---|
363 | // The entire SMM Configuration Protocol is allocated from SMRAM, so only a pointer
|
---|
364 | // to an SMRAM address will be present in the handle database
|
---|
365 | //
|
---|
366 | Status = SystemTable->BootServices->InstallMultipleProtocolInterfaces (
|
---|
367 | &gSmmCpuPrivate->SmmCpuHandle,
|
---|
368 | &gEfiSmmConfigurationProtocolGuid,
|
---|
369 | &gSmmCpuPrivate->SmmConfiguration,
|
---|
370 | NULL
|
---|
371 | );
|
---|
372 | ASSERT_EFI_ERROR (Status);
|
---|
373 |
|
---|
374 | //
|
---|
375 | // Expose address of CPU Hot Plug Data structure if CPU hot plug is supported.
|
---|
376 | //
|
---|
377 | if (FeaturePcdGet (PcdCpuHotPlugSupport)) {
|
---|
378 | Status = PcdSet64S (PcdCpuHotPlugDataAddress, (UINT64)(UINTN)&mCpuHotPlugData);
|
---|
379 | ASSERT_EFI_ERROR (Status);
|
---|
380 | }
|
---|
381 |
|
---|
382 | //
|
---|
383 | // Register SMM Ready To Lock Protocol notification
|
---|
384 | //
|
---|
385 | Status = gMmst->MmRegisterProtocolNotify (
|
---|
386 | &gEfiSmmReadyToLockProtocolGuid,
|
---|
387 | SmmReadyToLockEventNotify,
|
---|
388 | &Registration
|
---|
389 | );
|
---|
390 | ASSERT_EFI_ERROR (Status);
|
---|
391 |
|
---|
392 | return Status;
|
---|
393 | }
|
---|