blob: dfc1e357f1d9d6ec977d89dc60a04d11896596ef [file] [log] [blame]
Siyuan Wangaffe85f2013-07-25 15:14:15 +08001/* $NoKeywords:$ */
2/**
3 * @file
4 *
5 * mmflow.c
6 *
7 * Main Memory Flow Entrypoint file
8 *
9 * @xrefitem bom "File Content Label" "Release Content"
10 * @e project: AGESA
11 * @e sub-project: (Mem/Main)
12 * @e \$Revision: 84150 $ @e \$Date: 2012-12-12 15:46:25 -0600 (Wed, 12 Dec 2012) $
13 *
14 **/
15/*****************************************************************************
16*
17 * Copyright (c) 2008 - 2013, Advanced Micro Devices, Inc.
18 * All rights reserved.
19 *
20 * Redistribution and use in source and binary forms, with or without
21 * modification, are permitted provided that the following conditions are met:
22 * * Redistributions of source code must retain the above copyright
23 * notice, this list of conditions and the following disclaimer.
24 * * Redistributions in binary form must reproduce the above copyright
25 * notice, this list of conditions and the following disclaimer in the
26 * documentation and/or other materials provided with the distribution.
27 * * Neither the name of Advanced Micro Devices, Inc. nor the names of
28 * its contributors may be used to endorse or promote products derived
29 * from this software without specific prior written permission.
30 *
31 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
32 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
33 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
34 * DISCLAIMED. IN NO EVENT SHALL ADVANCED MICRO DEVICES, INC. BE LIABLE FOR ANY
35 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
36 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
37 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
38 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
39 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
40 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
41* ***************************************************************************
42*
43*/
44
45/*
46 *----------------------------------------------------------------------------
47 * MODULES USED
48 *
49 *----------------------------------------------------------------------------
50 */
51
52
53#include "AGESA.h"
54#include "amdlib.h"
55#include "AdvancedApi.h"
56#include "Ids.h"
57#include "cpuRegisters.h"
58#include "cpuServices.h"
59#include "GeneralServices.h"
60#include "cpuFamilyTranslation.h"
61#include "OptionMemory.h"
62#include "mm.h"
63#include "mn.h"
64#include "mt.h"
Mike Banonf7b410d2020-04-17 14:56:42 +030065#include "mtspd3.h"
Siyuan Wangaffe85f2013-07-25 15:14:15 +080066#include "mu.h"
67#include "heapManager.h"
68#include "Filecode.h"
69CODE_GROUP (G1_PEICC)
70RDATA_GROUP (G1_PEICC)
71
72#define FILECODE PROC_MEM_MAIN_MMFLOW_FILECODE
73/* features */
74
75extern MEM_NB_SUPPORT memNBInstalled[];
76extern MEM_TECH_CONSTRUCTOR* memTechInstalled[];
77extern MEM_FEAT_BLOCK_MAIN MemFeatMain;
78extern MEM_FLOW_CFG* memFlowControlInstalled[];
79
80/*----------------------------------------------------------------------------
81 * DEFINITIONS AND MACROS
82 *
83 *----------------------------------------------------------------------------
84 */
85
86/*----------------------------------------------------------------------------
87 * TYPEDEFS AND STRUCTURES
88 *
89 *----------------------------------------------------------------------------
90 */
91
92/*----------------------------------------------------------------------------
93 * PROTOTYPES OF LOCAL FUNCTIONS
94 *
95 *----------------------------------------------------------------------------
96 */
97VOID
98STATIC
99MemSPDDataProcess (
100 IN OUT MEM_DATA_STRUCT *MemPtr
101 );
102
Mike Banonf7b410d2020-04-17 14:56:42 +0300103#if (CONFIG(CPU_AMD_AGESA_OPENSOURCE_MEM_CUSTOM))
104
105VOID
106STATIC
107AgesaCustomMemoryProfileSPD (
108 IN OUT UINT8 *Buffer
109 );
110
111#endif
112
Siyuan Wangaffe85f2013-07-25 15:14:15 +0800113/*----------------------------------------------------------------------------
114 * EXPORTED FUNCTIONS
115 *
116 *----------------------------------------------------------------------------
117 */
118/* -----------------------------------------------------------------------------*/
119/**
120 *
121 *
122 * This function is the main memory configuration function for DR DDR3
123 *
124 * Requirements:
125 *
126 * Run-Time Requirements:
127 * 1. Complete Hypertransport Bus Configuration
128 * 2. AmdMemInitDataStructDef must be run to set default values
129 * 3. MSR bit to allow access to high PCI regs set on all nodes
130 * 4. BSP in Big Real Mode
131 * 5. Stack available
132 * 6. MCG_CTL=-1, MC4_EN=0 for all CPUs
133 * 7. MCi_STS from shutdown/warm reset recorded (if desired) prior to entry
134 * 8. All var MTRRs reset to zero
135 * 9. State of NB_CFG.DisDatMsk set properly on all CPUs
136 *
137 * @param[in,out] *MemPtr - Pointer to the MEM_DATA_STRUCT
138 *
139 * @return AGESA_STATUS
140 * - AGESA_ALERT
141 * - AGESA_FATAL
142 * - AGESA_SUCCESS
143 * - AGESA_WARNING
144 */
145AGESA_STATUS
146AmdMemAuto (
147 IN OUT MEM_DATA_STRUCT *MemPtr
148 )
149{
150 MEM_SHARED_DATA mmSharedData;
151 MEM_MAIN_DATA_BLOCK mmData;
152 MEM_NB_BLOCK *NBPtr;
153 MEM_TECH_BLOCK *TechPtr;
154 ALLOCATE_HEAP_PARAMS AllocHeapParams;
155 AGESA_STATUS Retval;
156 UINT8 i;
157 UINT8 Die;
158 UINT8 DieCount;
159 UINT8 Tab;
160 CPU_SPECIFIC_SERVICES *FamilySpecificServices;
161
162 ASSERT (MemPtr != NULL);
163
164 AGESA_TESTPOINT (TpProcMemAmdMemAuto, &MemPtr->StdHeader);
165 IDS_PERF_TIMESTAMP (TP_BEGINPROCAMDMEMAUTO, &MemPtr->StdHeader);
166
167 IDS_HDT_CONSOLE (MEM_FLOW, "MEM PARAMS:\n");
168 IDS_HDT_CONSOLE (MEM_FLOW, "\tBottomIo : %04x\n", MemPtr->ParameterListPtr->BottomIo);
169 IDS_HDT_CONSOLE (MEM_FLOW, "\tMemHoleRemap : %d\n", MemPtr->ParameterListPtr->MemHoleRemapping);
170 IDS_HDT_CONSOLE (MEM_FLOW, "\tLimitBelow1TB : %d\n", MemPtr->ParameterListPtr->LimitMemoryToBelow1Tb);
171 IDS_HDT_CONSOLE (MEM_FLOW, "\tUserTimingMode : %d\n", MemPtr->ParameterListPtr->UserTimingMode);
172 IDS_HDT_CONSOLE (MEM_FLOW, "\tMemClockValue : %d\n", MemPtr->ParameterListPtr->MemClockValue);
173 IDS_HDT_CONSOLE (MEM_FLOW, "\tBankIntlv : %d\n", MemPtr->ParameterListPtr->EnableBankIntlv);
174 IDS_HDT_CONSOLE (MEM_FLOW, "\tNodeIntlv : %d\n", MemPtr->ParameterListPtr->EnableNodeIntlv);
175 IDS_HDT_CONSOLE (MEM_FLOW, "\tChannelIntlv : %d\n", MemPtr->ParameterListPtr->EnableChannelIntlv);
176 IDS_HDT_CONSOLE (MEM_FLOW, "\tEccFeature : %d\n", MemPtr->ParameterListPtr->EnableEccFeature);
177 IDS_HDT_CONSOLE (MEM_FLOW, "\tPowerDown : %d\n", MemPtr->ParameterListPtr->EnablePowerDown);
178 IDS_HDT_CONSOLE (MEM_FLOW, "\tOnLineSpare : %d\n", MemPtr->ParameterListPtr->EnableOnLineSpareCtl);
179 IDS_HDT_CONSOLE (MEM_FLOW, "\tParity : %d\n", MemPtr->ParameterListPtr->EnableParity);
180 IDS_HDT_CONSOLE (MEM_FLOW, "\tBankSwizzle : %d\n", MemPtr->ParameterListPtr->EnableBankSwizzle);
181 IDS_HDT_CONSOLE (MEM_FLOW, "\tMemClr : %d\n", MemPtr->ParameterListPtr->EnableMemClr);
182 IDS_HDT_CONSOLE (MEM_FLOW, "\tUmaMode : %d\n", MemPtr->ParameterListPtr->UmaMode);
183 IDS_HDT_CONSOLE (MEM_FLOW, "\tUmaSize : %d\n", MemPtr->ParameterListPtr->UmaSize);
184 IDS_HDT_CONSOLE (MEM_FLOW, "\tMemRestoreCtl : %d\n", MemPtr->ParameterListPtr->MemRestoreCtl);
185 IDS_HDT_CONSOLE (MEM_FLOW, "\tSaveMemContextCtl : %d\n", MemPtr->ParameterListPtr->SaveMemContextCtl);
186 IDS_HDT_CONSOLE (MEM_FLOW, "\tExternalVrefCtl : %d\n", MemPtr->ParameterListPtr->ExternalVrefCtl );
187 IDS_HDT_CONSOLE (MEM_FLOW, "\tForceTrainMode : %d\n", MemPtr->ParameterListPtr->ForceTrainMode );
188 IDS_HDT_CONSOLE (MEM_FLOW, "\tAMP : %d\n\n", MemPtr->ParameterListPtr->AmpEnable);
189
190 //----------------------------------------------------------------------------
191 // Get TSC rate, which will be used later in Wait10ns routine
192 //----------------------------------------------------------------------------
193 GetCpuServicesOfCurrentCore ((CONST CPU_SPECIFIC_SERVICES **)&FamilySpecificServices, &MemPtr->StdHeader);
194 FamilySpecificServices->GetTscRate (FamilySpecificServices, &MemPtr->TscRate, &MemPtr->StdHeader);
195
196 //----------------------------------------------------------------------------
197 // Read In SPD Data
198 //----------------------------------------------------------------------------
199 AGESA_TESTPOINT (TpProcMemBeforeSpdProcessing, &MemPtr->StdHeader);
200 MemSPDDataProcess (MemPtr);
201
202 //----------------------------------------------------------------
203 // Initialize Main Data Block
204 //----------------------------------------------------------------
205 mmData.MemPtr = MemPtr;
206 mmData.mmSharedPtr = &mmSharedData;
207 LibAmdMemFill (&mmSharedData, 0, sizeof (mmSharedData), &MemPtr->StdHeader);
208 mmSharedData.DimmExcludeFlag = NORMAL;
209 mmSharedData.NodeIntlv.IsValid = FALSE;
210 //----------------------------------------------------------------
211 // Discover populated CPUs
212 //
213 //----------------------------------------------------------------
214 Retval = MemSocketScan (&mmData);
215 if (Retval == AGESA_FATAL) {
216 return Retval;
217 }
218 DieCount = mmData.DieCount;
219 //----------------------------------------------------------------
220 //
221 // Allocate Memory for NB and Tech Blocks
222 //
223 // NBPtr[Die]----+
224 // |
225 // V
226 // +---+---+---+---+---+---+---+---+
227 // | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | NB Blocks
228 // +---+---+---+---+---+---+---+---+
229 // | | | | | | | |
230 // | | | | | | | |
231 // v v v v v v v v
232 // +---+---+---+---+---+---+---+---+
233 // | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | Tech Blocks
234 // +---+---+---+---+---+---+---+---+
235 //
236 //
237 //----------------------------------------------------------------
238 AllocHeapParams.RequestedBufferSize = (DieCount * (sizeof (MEM_NB_BLOCK) + sizeof (MEM_TECH_BLOCK)));
239 AllocHeapParams.BufferHandle = AMD_MEM_AUTO_HANDLE;
240 AllocHeapParams.Persist = HEAP_LOCAL_CACHE;
241 if (AGESA_SUCCESS != HeapAllocateBuffer (&AllocHeapParams, &MemPtr->StdHeader)) {
242 ASSERT(FALSE); // NB and Tech Block Heap allocate error
243 return AGESA_FATAL;
244 }
245 NBPtr = (MEM_NB_BLOCK *)AllocHeapParams.BufferPtr;
246 TechPtr = (MEM_TECH_BLOCK *) (&NBPtr[DieCount]);
247 mmData.NBPtr = NBPtr;
248 mmData.TechPtr = TechPtr;
249
250 //----------------------------------------------------------------
251 // Create NB Blocks
252 //
253 //----------------------------------------------------------------
254 for (Die = 0 ; Die < DieCount ; Die++ ) {
255 i = 0;
256 while (memNBInstalled[i].MemConstructNBBlock != 0) {
257 if (memNBInstalled[i].MemConstructNBBlock (&NBPtr[Die], MemPtr, memNBInstalled[i].MemFeatBlock, &mmSharedData, Die) == TRUE) {
258 break;
259 }
260 i++;
261 }
262 // Couldn't find a NB which supported this family
263 if (memNBInstalled[i].MemConstructNBBlock == 0) {
264 return AGESA_FATAL;
265 }
266 }
267 //----------------------------------------------------------------
268 // Create Technology Blocks
269 //
270 //----------------------------------------------------------------
271 for (Die = 0 ; Die < DieCount ; Die++ ) {
272 i = 0;
273 while (memTechInstalled[i] != NULL) {
274 if (memTechInstalled[i] (&TechPtr[Die], &NBPtr[Die])) {
275 NBPtr[Die].TechPtr = &TechPtr[Die];
276 break;
277 }
278 i++;
279 }
280 // Couldn't find a Tech block which supported this family
281 if (memTechInstalled[i] == NULL) {
282 return AGESA_FATAL;
283 }
284 }
285
286 //----------------------------------------------------------------
287 // AMP initialization
288 //
289 //----------------------------------------------------------------
290 for (Die = 0 ; Die < DieCount ; Die++ ) {
291 NBPtr[Die].FeatPtr->InitAMP (&NBPtr[Die]);
292 }
293
294 //----------------------------------------------------------------
295 //
296 // MEMORY INITIALIZATION TASKS
297 //
298 //----------------------------------------------------------------
299 i = 0;
300 while (memFlowControlInstalled[i] != NULL) {
301 Retval = memFlowControlInstalled[i] (&mmData);
302 if (MemPtr->IsFlowControlSupported == TRUE) {
303 break;
304 }
305 i++;
306 }
307
308 //----------------------------------------------------------------
309 // Deallocate NB register tables
310 //----------------------------------------------------------------
311 for (Tab = 0; Tab < NumberOfNbRegTables; Tab++) {
312 HeapDeallocateBuffer (GENERATE_MEM_HANDLE (ALLOC_NB_REG_TABLE, Tab, 0, 0), &MemPtr->StdHeader);
313 }
314
315 //----------------------------------------------------------------
316 // Check for errors and return
317 //----------------------------------------------------------------
318 AGESA_TESTPOINT (TpProcMemEnd, &MemPtr->StdHeader);
319 IDS_PERF_TIMESTAMP (TP_ENDPROCAMDMEMAUTO, &MemPtr->StdHeader);
320 for (Die = 0; Die < DieCount; Die++) {
321 if (NBPtr[Die].MCTPtr->ErrCode > Retval) {
322 Retval = NBPtr[Die].MCTPtr->ErrCode;
323 }
324 }
325 return Retval;
326}
327
Mike Banonf7b410d2020-04-17 14:56:42 +0300328#if (CONFIG(CPU_AMD_AGESA_OPENSOURCE_MEM_CUSTOM))
329
330/* -----------------------------------------------------------------------------*/
331/**
332 *
333 *
334 * This function modifies a SPD buffer with the custom SPD values set up in coreboot's config.
335 *
336 * @param[in,out] *Buffer - Pointer to the UINT8
337 *
338 */
339
340VOID
341STATIC
342AgesaCustomMemoryProfileSPD (
343 IN OUT UINT8 *Buffer
344 )
345{
346 UINT16 Offset;
347 //
348 // Modify a SPD buffer.
349 //
350 Buffer[SPD_DIVIDENT] = CONFIG_CUSTOM_SPD_DIVIDENT;
351 Buffer[SPD_DIVISOR] = CONFIG_CUSTOM_SPD_DIVISOR;
352 Buffer[SPD_TCK] = CONFIG_CUSTOM_SPD_TCK;
353 Buffer[SPD_CASLO] = CONFIG_CUSTOM_SPD_CASLO;
354 Buffer[SPD_CASHI] = CONFIG_CUSTOM_SPD_CASHI;
355 Buffer[SPD_TAA] = CONFIG_CUSTOM_SPD_TAA;
356 Buffer[SPD_TWR] = CONFIG_CUSTOM_SPD_TWR;
357 Buffer[SPD_TRCD] = CONFIG_CUSTOM_SPD_TRCD;
358 Buffer[SPD_TRRD] = CONFIG_CUSTOM_SPD_TRRD;
359 Buffer[SPD_TRP] = CONFIG_CUSTOM_SPD_TRP;
360 //
361 // SPD_UPPER_TRC and SPD_UPPER_TRAS are the same index but different bits:
362 // SPD_UPPER_TRC - [7:4], SPD_UPPER_TRAS - [3:0].
363 //
364 Buffer[SPD_UPPER_TRAS] = (CONFIG_CUSTOM_SPD_UPPER_TRC << 4) + CONFIG_CUSTOM_SPD_UPPER_TRAS;
365 Buffer[SPD_TRAS] = CONFIG_CUSTOM_SPD_TRAS;
366 Buffer[SPD_TRC] = CONFIG_CUSTOM_SPD_TRC;
367 Buffer[SPD_TRFC_LO] = CONFIG_CUSTOM_SPD_TRFC_LO;
368 Buffer[SPD_TRFC_HI] = CONFIG_CUSTOM_SPD_TRFC_HI;
369 Buffer[SPD_TWTR] = CONFIG_CUSTOM_SPD_TWTR;
370 Buffer[SPD_TRTP] = CONFIG_CUSTOM_SPD_TRTP;
371 Buffer[SPD_UPPER_TFAW] = CONFIG_CUSTOM_SPD_UPPER_TFAW;
372 Buffer[SPD_TFAW] = CONFIG_CUSTOM_SPD_TFAW;
373 //
374 // Print a SPD buffer.
375 //
376 printk(BIOS_SPEW, "***** SPD BUFFER *****\n");
377 for (Offset = 0; Offset < 256; Offset++) {
378 printk(BIOS_SPEW, "Buffer[%d] = 0x%02X\n", Offset, Buffer[Offset]);
379 }
380 printk(BIOS_SPEW, "**********************\n");
381}
382
383#endif
Siyuan Wangaffe85f2013-07-25 15:14:15 +0800384
385/* -----------------------------------------------------------------------------*/
386/**
387 *
388 *
389 * This function fills a default SPD buffer with SPD values for all DIMMs installed in the system
390 *
391 * The SPD Buffer is populated with a Socket-Channel-Dimm centric view of the Dimms. At this
392 * point, the Memory controller type is not known, and the platform BIOS does not know the anything
393 * about which DIMM is on which DCT. So the DCT relationship is abstracted from the arrangement
394 * of SPD information here. We use the utility functions GetSpdSocketIndex(), GetMaxChannelsPerSocket(),
395 * and GetMaxDimmsPerChannel() to Map the SPD data according to which Socket-relative channel the DIMMs
396 * are connected to. The functions rely on either the maximum values in the
397 * PlatformSpecificOverridingTable or if unspecified, the absolute maximums in AGESA.H.
398 *
399 * This mapping is translated in the Northbridge object Constructor and the Technology block constructor.
400 *
401 * @param[in,out] *MemPtr - Pointer to the MEM_DATA_STRUCT
402 *
403 */
404
405VOID
406STATIC
407MemSPDDataProcess (
408 IN OUT MEM_DATA_STRUCT *MemPtr
409 )
410{
411 UINT8 Socket;
412 UINT8 Channel;
413 UINT8 Dimm;
414 UINT8 DimmIndex;
415 UINT32 AgesaStatus;
416 UINT8 MaxSockets;
417 UINT8 MaxChannelsPerSocket;
418 UINT8 MaxDimmsPerChannel;
419 SPD_DEF_STRUCT *DimmSPDPtr;
420 PSO_TABLE *PsoTable;
421 ALLOCATE_HEAP_PARAMS AllocHeapParams;
422 AGESA_READ_SPD_PARAMS SpdParam;
423
424 ASSERT (MemPtr != NULL);
425 MaxSockets = (UINT8) (0x000000FF & GetPlatformNumberOfSockets ());
426 PsoTable = MemPtr->ParameterListPtr->PlatformMemoryConfiguration;
427 //
428 // Allocate heap for the table
429 //
430 AllocHeapParams.RequestedBufferSize = (GetSpdSocketIndex (PsoTable, MaxSockets, &MemPtr->StdHeader) * sizeof (SPD_DEF_STRUCT));
431 AllocHeapParams.BufferHandle = AMD_MEM_SPD_HANDLE;
432 AllocHeapParams.Persist = HEAP_LOCAL_CACHE;
433 if (HeapAllocateBuffer (&AllocHeapParams, &MemPtr->StdHeader) == AGESA_SUCCESS) {
434 MemPtr->SpdDataStructure = (SPD_DEF_STRUCT *) AllocHeapParams.BufferPtr;
435 //
436 // Initialize SpdParam Structure
437 //
438 LibAmdMemCopy ((VOID *)&SpdParam, (VOID *)MemPtr, (UINTN)sizeof (SpdParam.StdHeader), &MemPtr->StdHeader);
439 //
440 // Populate SPDDataBuffer
441 //
442 SpdParam.MemData = MemPtr;
443 DimmIndex = 0;
444 for (Socket = 0; Socket < (UINT16)MaxSockets; Socket++) {
445 MaxChannelsPerSocket = GetMaxChannelsPerSocket (PsoTable, Socket, &MemPtr->StdHeader);
446 SpdParam.SocketId = Socket;
447 for (Channel = 0; Channel < MaxChannelsPerSocket; Channel++) {
448 SpdParam.MemChannelId = Channel;
449 MaxDimmsPerChannel = GetMaxDimmsPerChannel (PsoTable, Socket, Channel);
450 for (Dimm = 0; Dimm < MaxDimmsPerChannel; Dimm++) {
451 SpdParam.DimmId = Dimm;
452 DimmSPDPtr = &(MemPtr->SpdDataStructure[DimmIndex++]);
453 SpdParam.Buffer = DimmSPDPtr->Data;
454 AGESA_TESTPOINT (TpProcMemBeforeAgesaReadSpd, &MemPtr->StdHeader);
455 AgesaStatus = AgesaReadSpd (0, &SpdParam);
456 AGESA_TESTPOINT (TpProcMemAfterAgesaReadSpd, &MemPtr->StdHeader);
457 if (AgesaStatus == AGESA_SUCCESS) {
458 DimmSPDPtr->DimmPresent = TRUE;
459 IDS_HDT_CONSOLE (MEM_FLOW, "SPD Socket %d Channel %d Dimm %d: %08x\n", Socket, Channel, Dimm, SpdParam.Buffer);
Mike Banonf7b410d2020-04-17 14:56:42 +0300460 #if (CONFIG(CPU_AMD_AGESA_OPENSOURCE_MEM_CUSTOM))
461 AgesaCustomMemoryProfileSPD(SpdParam.Buffer);
462 #endif
Siyuan Wangaffe85f2013-07-25 15:14:15 +0800463 } else {
464 DimmSPDPtr->DimmPresent = FALSE;
465 }
466 }
467 }
468 }
469 } else {
470 PutEventLog (AGESA_FATAL, MEM_ERROR_HEAP_ALLOCATE_FOR_SPD, 0, 0, 0, 0, &MemPtr->StdHeader);
471 //
472 // Assert here if unable to allocate heap for SPDs
473 //
474 IDS_ERROR_TRAP;
475 }
476}