AGESA F15: AMD family15 AGESA code
[coreboot.git] / src / vendorcode / amd / agesa / f15 / Proc / HT / htMain.c
1 /* $NoKeywords:$ */
2 /**
3  * @file
4  *
5  * HyperTransport features and sequence implementation.
6  *
7  * Implements the external AmdHtInitialize entry point.
8  * Contains routines for directing the sequence of available features.
9  * Mostly, but not exclusively, AGESA_TESTPOINT invocations should be
10  * contained in this file, and not in the feature code.
11  *
12  * From a build option perspective, it may be that a few lines could be removed
13  * from compilation in this file for certain options.  It is considered that
14  * the code savings from this are too small to be of concern and this file
15  * should not have any explicit build option implementation.
16  *
17  * @xrefitem bom "File Content Label" "Release Content"
18  * @e project:      AGESA
19  * @e sub-project:  HyperTransport
20  * @e \$Revision: 56279 $   @e \$Date: 2011-07-11 13:11:28 -0600 (Mon, 11 Jul 2011) $
21  *
22  */
23 /*
24  *****************************************************************************
25  *
26  * Copyright (C) 2012 Advanced Micro Devices, Inc.
27  * All rights reserved.
28  *
29  * Redistribution and use in source and binary forms, with or without
30  * modification, are permitted provided that the following conditions are met:
31  *     * Redistributions of source code must retain the above copyright
32  *       notice, this list of conditions and the following disclaimer.
33  *     * Redistributions in binary form must reproduce the above copyright
34  *       notice, this list of conditions and the following disclaimer in the
35  *       documentation and/or other materials provided with the distribution.
36  *     * Neither the name of Advanced Micro Devices, Inc. nor the names of
37  *       its contributors may be used to endorse or promote products derived
38  *       from this software without specific prior written permission.
39  *
40  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
41  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
42  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
43  * DISCLAIMED. IN NO EVENT SHALL ADVANCED MICRO DEVICES, INC. BE LIABLE FOR ANY
44  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
45  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
46  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
47  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
48  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
49  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
50  *
51  * ***************************************************************************
52  *
53  */
54
55 /*
56  *----------------------------------------------------------------------------
57  *                                MODULES USED
58  *
59  *----------------------------------------------------------------------------
60  */
61
62
63
64 #include "AGESA.h"
65 #include "AdvancedApi.h"
66 #include "amdlib.h"
67 #include "Ids.h"
68 #include "Topology.h"
69 #include "htFeat.h"
70 #include "htInterface.h"
71 #include "htNb.h"
72 #include "heapManager.h"
73 #include "cpuServices.h"
74 #include "OptionsHt.h"
75 #include "Filecode.h"
76 CODE_GROUP (G1_PEICC)
77 RDATA_GROUP (G2_PEI)
78
79 #define FILECODE PROC_HT_HTMAIN_FILECODE
80 #define APIC_Base_BSP    8
81 #define APIC_Base        0x1b
82
83 extern OPTION_HT_CONFIGURATION OptionHtConfiguration;
84
85 BOOLEAN
86 STATIC
87 IsBootCore (
88   IN       STATE_DATA    *State
89   );
90
91 /*----------------------------------------------------------------------------------------*/
92 /**
93  * Update maps with the core range for each module.
94  *
95  * Cores are numbered relative to a Processor, but sometimes there is a need to know the
96  * starting and ending core ids on a particular node.  This same info is also useful for
97  * supporting the Core count on a node other than the one currently executing.
98  *
99  * For each Processor, get the core count of each node using the family specific PCI core count
100  * interface. The order of cores in a processor, and whether it is special for the BSP is family
101  * specific.  But whether the processor orders core ids by module or node, iterate in the right
102  * order and use the counts to determine each start and end range.
103  *
104  * Update compute unit status for each node.
105  *
106  * @param[in]   State    number of Nodes discovered.
107 */
108 VOID
109 STATIC
110 UpdateCoreRanges (
111   IN       STATE_DATA    *State
112     )
113 {
114   UINT8 Node;
115   UINT8 ProcessorCores;
116   UINT8 ModuleCoreCount[MAX_DIES];
117   UINT8 Socket;
118   UINT8 Module;
119
120   ASSERT (State->SocketDieToNodeMap != NULL);
121   ASSERT (State->NodeToSocketDieMap != NULL);
122
123   for (Socket = 0; Socket < MAX_SOCKETS; Socket++) {
124     // Is a Processor present in Socket?
125     if ((*State->SocketDieToNodeMap)[Socket][0].Node != HT_LIST_TERMINAL) {
126       // Get all the Module core counts for this processor
127       // Note that the core counts are 1 based counts.
128       // Since Compute Unit info is not module ordering dependent, write it now.
129       for (Module = 0; Module < MAX_DIES; Module++) {
130         if ((*State->SocketDieToNodeMap)[Socket][Module].Node != HT_LIST_TERMINAL) {
131           ModuleCoreCount[Module] = State->Nb->GetNumCoresOnNode ((*State->SocketDieToNodeMap)[Socket][Module].Node, State->Nb);
132           (*State->SocketDieToNodeMap)[Socket][Module].EnabledComputeUnits =
133             State->Nb->GetEnabledComputeUnits ((*State->SocketDieToNodeMap)[Socket][Module].Node, State->Nb);
134           (*State->SocketDieToNodeMap)[Socket][Module].DualCoreComputeUnits =
135             State->Nb->GetDualCoreComputeUnits ((*State->SocketDieToNodeMap)[Socket][Module].Node, State->Nb);
136         } else {
137           ModuleCoreCount[Module] = 0;
138         }
139       }
140       // Determine the core ordering rule for this processor.
141       if ((((*State->NodeToSocketDieMap)[0].Socket == Socket) && State->Nb->IsOrderBSPCoresByNode) ||
142           (!State->Nb->IsOrderCoresByModule)) {
143         // Order core ranges on this processor by Node Id.
144         ProcessorCores = 0;
145         for (Node = 0; Node < State->Nb->GetNodeCount (State->Nb); Node++) {
146           // Is this node a module in this processor?
147           if ((*State->NodeToSocketDieMap)[Node].Socket == Socket) {
148             Module = (*State->NodeToSocketDieMap)[Node].Die;
149             if (ModuleCoreCount[Module] != 0) {
150               (*State->SocketDieToNodeMap)[Socket][Module].LowCore = ProcessorCores;
151               (*State->SocketDieToNodeMap)[Socket][Module].HighCore = ProcessorCores + (ModuleCoreCount[Module] - 1);
152               IDS_HDT_CONSOLE (
153                 HT_TRACE,
154                 (IsBootCore (State) ?
155                  "Topology: Socket %d, Die %d, is Node %d, with Cores %d thru %d. Compute Unit status (0x%x,0x%x).\n" :
156                  ""),
157                 Socket,
158                 Module,
159                 Node,
160                 (*State->SocketDieToNodeMap)[Socket][Module].LowCore,
161                 (*State->SocketDieToNodeMap)[Socket][Module].HighCore,
162                 (*State->SocketDieToNodeMap)[Socket][Module].EnabledComputeUnits,
163                 (*State->SocketDieToNodeMap)[Socket][Module].DualCoreComputeUnits
164                 );
165               ProcessorCores = ProcessorCores + ModuleCoreCount[Module];
166             }
167           }
168         }
169       } else {
170         // Order core ranges in this processor by Module Id.
171         ProcessorCores = 0;
172         for (Module = 0; Module < MAX_DIES; Module++) {
173           if (ModuleCoreCount[Module] != 0) {
174             (*State->SocketDieToNodeMap)[Socket][Module].LowCore = ProcessorCores;
175             (*State->SocketDieToNodeMap)[Socket][Module].HighCore = ProcessorCores + (ModuleCoreCount[Module] - 1);
176               IDS_HDT_CONSOLE (
177                 HT_TRACE,
178                 (IsBootCore (State) ?
179                  "Topology: Socket %d, Die %d, is Node %d, with Cores %d thru %d. Compute Unit status (0x%x,0x%x).\n" :
180                  ""),
181                 Socket,
182                 Module,
183                 (*State->SocketDieToNodeMap)[Socket][Module].Node,
184                 (*State->SocketDieToNodeMap)[Socket][Module].LowCore,
185                 (*State->SocketDieToNodeMap)[Socket][Module].HighCore,
186                 (*State->SocketDieToNodeMap)[Socket][Module].EnabledComputeUnits,
187                 (*State->SocketDieToNodeMap)[Socket][Module].DualCoreComputeUnits
188                 );
189             ProcessorCores = ProcessorCores + ModuleCoreCount[Module];
190           }
191         }
192       }
193     }
194   }
195 }
196
197 /*----------------------------------------------------------------------------------------*/
198 /**
199  * Complete the coherent init with any system level initialization.
200  *
201  * Find the total number of cores and update the number of Nodes and cores in all cpus.
202  * Limit cpu config access to installed cpus.
203  *
204  * @param[in]   State    number of Nodes discovered.
205 */
206 VOID
207 STATIC
208 FinalizeCoherentInit (
209   IN       STATE_DATA    *State
210     )
211 {
212   UINT8 Node;
213   UINT8 TotalCores;
214
215   TotalCores = 0;
216
217   for (Node = 0; Node < (State->NodesDiscovered + 1); Node++) {
218     TotalCores = TotalCores + State->Nb->GetNumCoresOnNode (Node, State->Nb);
219   }
220
221   for (Node = 0; Node < (State->NodesDiscovered + 1); Node++) {
222     State->Nb->SetTotalNodesAndCores (Node, State->NodesDiscovered + 1, TotalCores, State->Nb);
223   }
224
225   // Set all nodes to limit config space based on node count, after all nodes have a valid count.
226   // (just being cautious, probably we could combine the loops.)
227   for (Node = 0; Node < (State->NodesDiscovered + 1); Node++) {
228     State->Nb->LimitNodes (Node, State->Nb);
229   }
230 }
231
232 /*----------------------------------------------------------------------------------------*/
233 /**
234  * Initialize the coherent fabric.
235  *
236  * Perform discovery and initialization of the coherent fabric, for builds including
237  * support for multiple coherent nodes.
238  *
239  * @param[in]   State   global state
240  */
241 VOID
242 STATIC
243 CoherentInit (
244   IN OUT   STATE_DATA    *State
245   )
246 {
247   UINT8 i;
248   UINT8 j;
249   UINT8 ModuleType;
250   UINT8 Module;
251   UINT8 HardwareSocket;
252   COHERENT_FABRIC Fabric;
253
254   // Because Node 0, the BSP, is not discovered, initialize info about it specially here.
255   // Allocate Socket Die Map.
256   // While the BSP is always capable of being the only processor in the system, call the
257   // IsExceededCapable method to make sure the BSP's capability is included in the aggregate system
258   // capability. We don't care to check the return value.
259   //
260   State->Fabric = &Fabric;
261   State->NodesDiscovered = 0;
262   State->TotalLinks = 0;
263   State->SysMpCap = MAX_NODES;
264   State->Nb->IsExceededCapable (0, State, State->Nb);
265   HardwareSocket = State->Nb->GetSocket (0, 0, State->Nb);
266   ModuleType = 0;
267   Module = 0;
268   State->Nb->GetModuleInfo (0, &ModuleType, &Module, State->Nb);
269   // No predecessor info for BSP, so pass 0xFF for those parameters.
270   State->HtInterface->SetNodeToSocketMap (0xFF, 0xFF, 0xFF, 0, HardwareSocket, Module, State);
271
272   // Initialize system state data structures
273   for (i = 0; i < MAX_NODES; i++) {
274     State->Fabric->SysDegree[i] = 0;
275     for (j = 0; j < MAX_NODES; j++) {
276       State->Fabric->SysMatrix[i][j] = 0;
277     }
278   }
279
280   //
281   // Call the coherent init features
282   //
283
284   // Discovery
285   State->HtFeatures->CoherentDiscovery (State);
286   State->HtInterface->PostMapToAp (State);
287   // Topology matching and Routing
288   AGESA_TESTPOINT (TpProcHtTopology, State->ConfigHandle);
289   State->HtFeatures->LookupComputeAndLoadRoutingTables (State);
290   State->HtFeatures->MakeHopCountTable (State);
291
292   // UpdateCoreRanges requires the other maps to be initialized, and the node count set.
293   FinalizeCoherentInit (State);
294   UpdateCoreRanges (State);
295   State->Fabric = NULL;
296 }
297
298 /***************************************************************************
299  ***                       Non-coherent init code                        ***
300  ***                             Algorithms                              ***
301  ***************************************************************************/
302 /*----------------------------------------------------------------------------------------*/
303 /**
304  * Initialize the non-coherent fabric.
305  *
306  * Begin with the Compat Link on the BSP, then find and initialize all other
307  * non-coherent chains.
308  *
309  * @param[in]   State    our global state
310  */
311 VOID
312 STATIC
313 NcInit (
314   IN       STATE_DATA    *State
315   )
316 {
317   UINT8 Node;
318   UINT8 Link;
319   UINT8 CompatLink;
320   FINAL_LINK_STATE FinalLinkState;
321
322   // Initialize the southbridge chain.
323   State->AutoBusCurrent = State->HtBlock->AutoBusStart;
324   State->UsedCfgMapEntries = 0;
325   CompatLink = State->Nb->ReadSouthbridgeLink (State->Nb);
326   State->HtFeatures->ProcessLink (0, CompatLink, TRUE, State);
327
328   // Find and initialize all other non-coherent chains.
329   for (Node = 0; Node <= State->NodesDiscovered; Node++) {
330     for (Link = 0; Link < State->Nb->MaxLinks; Link++) {
331       // Skip the Link, if any of these tests indicate
332       FinalLinkState = State->HtInterface->GetIgnoreLink (Node, Link, State->Nb->DefaultIgnoreLinkList, State);
333       if (FinalLinkState == UNMATCHED) {
334         if ( !((Node == 0) && (Link == CompatLink))) {
335           if ( !(State->Nb->ReadTrueLinkFailStatus (Node, Link, State, State->Nb))) {
336             if (State->Nb->VerifyLinkIsNonCoherent (Node, Link, State->Nb)) {
337               State->HtFeatures->ProcessLink (Node, Link, FALSE, State);
338             }
339           }
340         }
341       }
342     }
343   }
344 }
345
346 /***************************************************************************
347  ***                            Link Optimization                        ***
348  ***************************************************************************/
349
350 /*----------------------------------------------------------------------------------------*/
351 /**
352  * Optimize Link Features.
353  *
354  * Based on Link capabilities, apply optimization rules to come up with the best
355  * settings, including several external limit decision from the interface. This includes
356  * handling of subLinks.  Finally, after the port list data is updated, set the hardware
357  * state for all Links.
358  *
359  * @param[in] State our global state
360  */
361 VOID
362 STATIC
363 LinkOptimization (
364   IN       STATE_DATA *State
365   )
366 {
367   AGESA_TESTPOINT (TpProcHtOptGather, State->ConfigHandle);
368   State->HtFeatures->GatherLinkData (State);
369
370   AGESA_TESTPOINT (TpProcHtOptRegang, State->ConfigHandle);
371   State->HtFeatures->RegangLinks (State);
372
373   AGESA_TESTPOINT (TpProcHtOptLinks, State->ConfigHandle);
374   State->HtFeatures->SelectOptimalWidthAndFrequency (State);
375
376   // A likely cause of mixed Retry settings on coherent links is sublink ratio balancing
377   // so check this after doing the sublinks.
378   AGESA_TESTPOINT (TpProcHtOptSubLinks, State->ConfigHandle);
379   State->HtFeatures->SubLinkRatioFixup (State);
380   if (State->HtFeatures->IsCoherentRetryFixup (State)) {
381     // Fix sublinks again within HT1 only frequencies, as ratios may be invalid again.
382     State->HtFeatures->SubLinkRatioFixup (State);
383   }
384
385   AGESA_TESTPOINT (TpProcHtOptFinish, State->ConfigHandle);
386   State->HtFeatures->SetLinkData (State);
387 }
388
389 /*----------------------------------------------------------------------------------------*/
390 /**
391  * Handle system and performance tunings.
392  *
393  * Including traffic distribution, fifo and
394  * buffer tuning that can't be placed in the register table,
395  * and special config tunings.
396  *
397  * @param[in] State    Total Nodes, port list data
398  */
399 VOID
400 STATIC
401 Tuning (
402   IN       STATE_DATA *State
403   )
404 {
405   UINT8 Node;
406
407   // For each Node, invoke northbridge specific buffer tunings that can not be done in reg table.
408   //
409   AGESA_TESTPOINT (TpProcHtTuning, State->ConfigHandle);
410   for (Node = 0; Node < (State->NodesDiscovered + 1); Node++) {
411     State->Nb->BufferOptimizations (Node, State, State->Nb);
412   }
413
414   // See if traffic distribution can be done and do it if so.
415   //
416   AGESA_TESTPOINT (TpProcHtTrafficDist, State->ConfigHandle);
417   State->HtFeatures->TrafficDistribution (State);
418 }
419
420 /*----------------------------------------------------------------------------------------*/
421 /**
422  * Initialize the Node and Socket maps for an AP Core.
423  *
424  * In each core's local heap, create a Node to Socket map and a Socket/Module to Node map.
425  * The mapping is filled in by reading the AP Mailboxes from PCI config on each node.
426  *
427  * @param[in]    State    global state, input data
428  *
429  */
430 VOID
431 STATIC
432 InitApMaps (
433   IN       STATE_DATA *State
434   )
435 {
436   UINT8 Node;
437   AP_MAIL_INFO NodeApMailBox;
438
439   // There is no option to not have socket - node maps, if they aren't allocated that is a fatal bug.
440   ASSERT (State->SocketDieToNodeMap != NULL);
441   ASSERT (State->NodeToSocketDieMap != NULL);
442
443   for (Node = 0; Node < State->Nb->GetNodeCount (State->Nb); Node++) {
444     NodeApMailBox = State->Nb->RetrieveMailbox (Node, State->Nb);
445     (*State->SocketDieToNodeMap)[NodeApMailBox.Fields.Socket][NodeApMailBox.Fields.Module].Node = Node;
446     (*State->NodeToSocketDieMap)[Node].Socket = (UINT8)NodeApMailBox.Fields.Socket;
447     (*State->NodeToSocketDieMap)[Node].Die = (UINT8)NodeApMailBox.Fields.Module;
448   }
449   // This requires the other maps to be initialized.
450   UpdateCoreRanges (State);
451 }
452
453 /*----------------------------------------------------------------------------------------*/
454 /**
455  * Is the currently running core the BSC?
456  *
457  * Determine whether the init steps for BSC or AP core should be run.
458  *
459  * @param[in]    State    global state, input data
460  *
461  * @retval       TRUE    This is the boot core.
462  * @retval       FALSE   This is not the boot core.
463  */
464 BOOLEAN
465 STATIC
466 IsBootCore (
467   IN       STATE_DATA    *State
468   )
469 {
470   UINT64 Value;
471
472   LibAmdMsrRead (APIC_Base, &Value, State->ConfigHandle);
473
474   return ((BOOLEAN) (((UINT32) (Value & 0xFFFFFFFF) & ((UINT32)1 << APIC_Base_BSP)) != 0));
475 }
476
477 /***************************************************************************
478  ***                            HT Initialize                             ***
479  ***************************************************************************/
480
481 /*----------------------------------------------------------------------------------------*/
482 /**
483  * The top level external interface for Hypertransport Initialization.
484  *
485  * Create our initial internal state, initialize the coherent fabric,
486  * initialize the non-coherent chains, and perform any required fabric tuning or
487  * optimization.
488  *
489  * @param[in]   StdHeader              Opaque handle to standard config header
490  * @param[in]   PlatformConfiguration  The platform configuration options.
491  * @param[in]   AmdHtInterface         HT Interface structure.
492  *
493  * @retval      AGESA_SUCCESS     Only information events logged.
494  * @retval      AGESA_ALERT       Sync Flood or CRC error logged.
495  * @retval      AGESA_WARNING     Example: expected capability not found
496  * @retval      AGESA_ERROR       logged events indicating some devices may not be available
497  * @retval      AGESA_FATAL       Mixed Family or MP capability mismatch
498  *
499  */
500 AGESA_STATUS
501 AmdHtInitialize (
502   IN       AMD_CONFIG_PARAMS      *StdHeader,
503   IN       PLATFORM_CONFIGURATION *PlatformConfiguration,
504   IN       AMD_HT_INTERFACE       *AmdHtInterface
505   )
506 {
507   STATE_DATA State;
508   NORTHBRIDGE Nb;
509   HT_FEATURES HtFeatures;
510   HT_INTERFACE HtInterface;
511   AGESA_STATUS DeallocateStatus;
512   AP_MAIL_INFO ApMailboxInfo;
513   UINT8 ApNode;
514
515   ALLOCATE_HEAP_PARAMS AllocHeapParams;
516
517   State.HtBlock = AmdHtInterface;
518   State.ConfigHandle = StdHeader;
519   State.PlatformConfiguration = PlatformConfiguration;
520
521   // Get the current HT internal interface (to HtBlock data)
522   NewHtInterface (&HtInterface, State.ConfigHandle);
523   State.HtInterface = &HtInterface;
524
525   // Get the current HT Feature Set
526   NewHtFeatures (&HtFeatures, State.ConfigHandle);
527   State.HtFeatures = &HtFeatures;
528
529   // Initialize from static options
530   State.IsUsingRecoveryHt = OptionHtConfiguration.IsUsingRecoveryHt;
531   State.IsSetHtCrcFlood = OptionHtConfiguration.IsSetHtCrcFlood;
532   State.IsUsingUnitIdClumping = OptionHtConfiguration.IsUsingUnitIdClumping;
533
534   // Initialize for status and event output
535   State.MaxEventClass = AGESA_SUCCESS;
536
537   // Allocate permanent heap structs that are interfaces to other AGESA services.
538   State.HtInterface->NewNodeAndSocketTables (&State);
539
540   if (IsBootCore (&State)) {
541     AGESA_TESTPOINT (TpProcHtEntry, State.ConfigHandle);
542     // Allocate Bsp only interface heap structs.
543     State.HtInterface->NewHopCountTable (&State);
544     // Allocate heap for our temporary working space.
545     AllocHeapParams.RequestedBufferSize = (sizeof (PORT_DESCRIPTOR) * (MAX_PLATFORM_LINKS * 2));
546     AllocHeapParams.BufferHandle = HT_STATE_DATA_HANDLE;
547     AllocHeapParams.Persist = HEAP_LOCAL_CACHE;
548     if (HeapAllocateBuffer (&AllocHeapParams, State.ConfigHandle) == AGESA_SUCCESS) {
549       State.PortList = (PORT_LIST)AllocHeapParams.BufferPtr;
550       // Create the BSP's northbridge.
551       NewNorthBridge (0, &State, &Nb);
552       State.Nb = &Nb;
553
554       CoherentInit (&State);
555       NcInit (&State);
556       LinkOptimization (&State);
557       Tuning (&State);
558
559       DeallocateStatus = HeapDeallocateBuffer (HT_STATE_DATA_HANDLE, State.ConfigHandle);
560       ASSERT (DeallocateStatus == AGESA_SUCCESS);
561       AGESA_TESTPOINT (TpProcHtDone, State.ConfigHandle);
562     } else {
563       ASSERT (FALSE);
564       State.MaxEventClass = AGESA_ERROR;
565       // Cannot Log entry due to heap allocate failed.
566     }
567   } else {
568     // Do the AP HT Init, which produces Node and Socket Maps for the AP's use.
569     AGESA_TESTPOINT (TpProcHtApMapEntry, State.ConfigHandle);
570     GetApMailbox (&ApMailboxInfo.Info, State.ConfigHandle);
571     ASSERT (ApMailboxInfo.Fields.Node < MAX_NODES);
572     ApNode = (UINT8)ApMailboxInfo.Fields.Node;
573     NewNorthBridge (ApNode, &State, &Nb);
574     State.Nb = &Nb;
575     InitApMaps (&State);
576     AGESA_TESTPOINT (TpProcHtApMapDone, State.ConfigHandle);
577   }
578   return State.MaxEventClass;
579 }