summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/include/usr/targeting/targplatutil.H16
-rw-r--r--src/usr/isteps/istep14/call_host_mpipl_service.C288
-rw-r--r--src/usr/isteps/istep14/call_proc_htm_setup.C2
-rw-r--r--src/usr/isteps/istep18/establish_system_smp.C32
-rw-r--r--src/usr/runtime/populate_hbruntime.C26
-rwxr-xr-xsrc/usr/targeting/common/xmltohb/attribute_types_hb.xml15
-rw-r--r--src/usr/targeting/common/xmltohb/target_types_hb.xml3
-rw-r--r--src/usr/targeting/targplatutil.C15
8 files changed, 241 insertions, 156 deletions
diff --git a/src/include/usr/targeting/targplatutil.H b/src/include/usr/targeting/targplatutil.H
index 198be1423..fd9bf9d90 100644
--- a/src/include/usr/targeting/targplatutil.H
+++ b/src/include/usr/targeting/targplatutil.H
@@ -5,7 +5,7 @@
/* */
/* OpenPOWER HostBoot Project */
/* */
-/* Contributors Listed Below - COPYRIGHT 2013,2017 */
+/* Contributors Listed Below - COPYRIGHT 2013,2018 */
/* [+] International Business Machines Corp. */
/* */
/* */
@@ -184,6 +184,20 @@ inline bool isThisMasterNodeTarget(const Target* const i_pTarget)
return true;
}
+/* @brief - returns whether the current node is the master node
+ *
+ * @par Detailed Description:
+ * Checks whether the current node is the master node. If yes, returns
+ * true to the user, else false. Master node is not determined until
+ * istep 18.9, so this function will return false before then except
+ * in the case of a MPIPL (second pass).
+ *
+ * @return boolean indicating whether request was successful or not
+ * @retval, Returns true if the current node is the master node
+ * @retval, Returns false if the current node is not the master node
+ */
+bool isCurrentMasterNode();
+
/* @brief - Syncs the master system target's attribute with non-master system
* targets.
*
diff --git a/src/usr/isteps/istep14/call_host_mpipl_service.C b/src/usr/isteps/istep14/call_host_mpipl_service.C
index 3e2ecdb1a..6a65bf308 100644
--- a/src/usr/isteps/istep14/call_host_mpipl_service.C
+++ b/src/usr/isteps/istep14/call_host_mpipl_service.C
@@ -33,9 +33,10 @@
#include <targeting/common/commontargeting.H>
#include <targeting/common/util.H>
#include <targeting/common/utilFilter.H>
+#include <targeting/targplatutil.H>
#include <p9_mpipl_chip_cleanup.H>
-#include <fapi2/plat_hwp_invoker.H>
+#include <fapi2/plat_hwp_invoker.H>
#include <vfs/vfs.H>
#include <dump/dumpif.H>
@@ -58,191 +59,202 @@ void* call_host_mpipl_service (void *io_pArgs)
IStepError l_StepError;
- TRACFCOMP( ISTEPS_TRACE::g_trac_isteps_trace,
- "call_host_mpipl_service entry" );
-
- errlHndl_t l_err = NULL;
- // call proc_mpipl_chip_cleanup.C
- TARGETING::TargetHandleList l_procTargetList;
- getAllChips(l_procTargetList, TYPE_PROC );
+ TRACFCOMP(ISTEPS_TRACE::g_trac_isteps_trace,
+ "call_host_mpipl_service entry" );
- // ---------------------------------------------------------------
- // run proc_mpipl_chip_cleanup.C on all proc chips
- // ---------------------------------------------------------------
- for (const auto & l_pProcTarget : l_procTargetList)
+ if (!TARGETING::UTIL::isCurrentMasterNode())
{
- // write HUID of target
TRACFCOMP(ISTEPS_TRACE::g_trac_isteps_trace,
- "target HUID %.8X", TARGETING::get_huid(l_pProcTarget));
+ "call_host_mpipl_service cannot run on slave node, skipping");
+ }
+ else
+ {
+ errlHndl_t l_err = NULL;
+ // call proc_mpipl_chip_cleanup.C
+ TARGETING::TargetHandleList l_procTargetList;
+ getAllChips(l_procTargetList, TYPE_PROC );
+
+ // ---------------------------------------------------------------
+ // run proc_mpipl_chip_cleanup.C on all proc chips
+ // ---------------------------------------------------------------
+ for (const auto & l_pProcTarget : l_procTargetList)
+ {
+ // write HUID of target
+ TRACFCOMP(ISTEPS_TRACE::g_trac_isteps_trace,
+ "target HUID %.8X", TARGETING::get_huid(l_pProcTarget));
- fapi2::Target<fapi2::TARGET_TYPE_PROC_CHIP> l_fapi_pProcTarget((const_cast<TARGETING::Target*> (l_pProcTarget)) );
+ fapi2::Target<fapi2::TARGET_TYPE_PROC_CHIP> l_fapi_pProcTarget((const_cast<TARGETING::Target*> (l_pProcTarget)) );
- // call the HWP with each fapi::Target
- FAPI_INVOKE_HWP(l_err, p9_mpipl_chip_cleanup, l_fapi_pProcTarget );
+ // call the HWP with each fapi::Target
+ FAPI_INVOKE_HWP(l_err, p9_mpipl_chip_cleanup, l_fapi_pProcTarget );
- if ( l_err )
- {
- TRACFCOMP(ISTEPS_TRACE::g_trac_isteps_trace,
- "ERROR : returned from p9_mpipl_chip_cleanup" );
+ if ( l_err )
+ {
+ TRACFCOMP(ISTEPS_TRACE::g_trac_isteps_trace,
+ "ERROR : returned from p9_mpipl_chip_cleanup" );
- // capture the target data in the elog
- ERRORLOG::ErrlUserDetailsTarget(l_pProcTarget).addToLog( l_err );
+ // capture the target data in the elog
+ ERRORLOG::ErrlUserDetailsTarget(l_pProcTarget).addToLog(l_err);
- // since we are doing an mpipl break out, the mpipl has failed
- break;
- }
+ // since we are doing an mpipl break out, the mpipl has failed
+ break;
+ }
- }
+ }
#ifdef CONFIG_DRTM
- if(!l_err)
- {
- do {
-
- bool drtmMpipl = false;
- SECUREBOOT::DRTM::isDrtmMpipl(drtmMpipl);
- if(drtmMpipl)
+ if(!l_err)
{
- l_err = SECUREBOOT::DRTM::validateDrtmPayload();
- if(l_err)
- {
- TRACFCOMP(ISTEPS_TRACE::g_trac_isteps_trace, ERR_MRK
- "call_host_mpipl_service: Failed in call to "
- "validateDrtmPayload()");
- break;
- }
+ do {
- l_err = SECUREBOOT::DRTM::completeDrtm();
- if(l_err)
+ bool drtmMpipl = false;
+ SECUREBOOT::DRTM::isDrtmMpipl(drtmMpipl);
+ if(drtmMpipl)
{
- TRACFCOMP(ISTEPS_TRACE::g_trac_isteps_trace, ERR_MRK
- "call_host_mpipl_service: Failed in call to "
- "completeDrtm()" );
- break;
+ l_err = SECUREBOOT::DRTM::validateDrtmPayload();
+ if(l_err)
+ {
+ TRACFCOMP(ISTEPS_TRACE::g_trac_isteps_trace, ERR_MRK
+ "call_host_mpipl_service: Failed in call to "
+ "validateDrtmPayload()");
+ break;
+ }
+
+ l_err = SECUREBOOT::DRTM::completeDrtm();
+ if(l_err)
+ {
+ TRACFCOMP(ISTEPS_TRACE::g_trac_isteps_trace, ERR_MRK
+ "call_host_mpipl_service: Failed in call to "
+ "completeDrtm()" );
+ break;
+ }
}
- }
- } while(0);
- }
+ } while(0);
+ }
#endif
- // No error on the procedure.. proceed to collect the dump.
- if (!l_err)
- {
-
- TRACFCOMP( ISTEPS_TRACE::g_trac_isteps_trace,
- "SUCCESS : proc_mpipl_ex_cleanup" );
+ // No error on the procedure.. proceed to collect the dump.
+ if (!l_err)
+ {
- // currently according to Adriana, the dump calls should only cause an
- // istep failure when the dump collect portion of this step fails.. We
- // will not fail the istep on any mbox message failures. instead we will
- // simply commit the errorlog and continue.
+ TRACFCOMP( ISTEPS_TRACE::g_trac_isteps_trace,
+ "SUCCESS : proc_mpipl_ex_cleanup" );
- errlHndl_t l_errMsg = NULL;
+ // currently according to Adriana, the dump calls should only cause
+ // an istep failure when the dump collect portion of this step
+ // fails.. We will not fail the istep on any mbox message failures.
+ // instead we will simply commit the errorlog and continue.
- // Dump relies upon the runtime module
- // Not declaring in istep DEP list cause if we load it
- // we want it to stay loaded
- if ( !VFS::module_is_loaded( "libruntime.so" ) )
- {
- l_err = VFS::module_load( "libruntime.so" );
+ errlHndl_t l_errMsg = NULL;
- if ( l_err )
+ // Dump relies upon the runtime module
+ // Not declaring in istep DEP list cause if we load it
+ // we want it to stay loaded
+ if ( !VFS::module_is_loaded( "libruntime.so" ) )
{
- // load module returned with errl set
- TRACFCOMP( ISTEPS_TRACE::g_trac_isteps_trace,
- "Could not load runtime module" );
+ l_err = VFS::module_load( "libruntime.so" );
+
+ if ( l_err )
+ {
+ // load module returned with errl set
+ TRACFCOMP( ISTEPS_TRACE::g_trac_isteps_trace,
+ "Could not load runtime module" );
+ }
}
- }
- // If dump module successfull loaded then continue with DumpCollect and
- // messaging
- if (!l_err)
- {
- do
+ // If dump module successfully loaded then continue with DumpCollect
+ // and messaging
+ if (!l_err)
{
- // send the start message
- l_errMsg = DUMP::sendMboxMsg(DUMP::DUMP_MSG_START_MSG_TYPE);
-
- // If error, commit and send error message.
- if (l_errMsg)
+ do
{
- TRACFCOMP(ISTEPS_TRACE::g_trac_isteps_trace,
+ // send the start message
+ l_errMsg = DUMP::sendMboxMsg(DUMP::DUMP_MSG_START_MSG_TYPE);
+
+ // If error, commit and send error message.
+ if (l_errMsg)
+ {
+ TRACFCOMP(ISTEPS_TRACE::g_trac_isteps_trace,
"ERROR : returned from DUMP::sendMboxMsg - dump start" );
- errlCommit( l_errMsg, HWPF_COMP_ID );
+ errlCommit( l_errMsg, HWPF_COMP_ID );
- // don't break in this case because we not want to fail the
- // istep on the dump collect so we will continue after we
- // log the errhandle that we can't send a message.
- }
+ // don't break in this case because we not want to fail
+ // the istep on the dump collect so we will continue
+ // after we log the errhandle that we can't send a
+ // message.
+ }
- // Call the dump collect
- l_err = DUMP::doDumpCollect();
+ // Call the dump collect
+ l_err = DUMP::doDumpCollect();
- // Got a Dump Collect error.. Commit the dumpCollect
- // errorlog and then send an dump Error mbox message
- // and FSP will decide what to do.
- // We do not want dump Collect failures to terminate the istep.
- if (l_err)
- {
- TRACFCOMP(ISTEPS_TRACE::g_trac_isteps_trace,
- "ERROR : returned from DUMP::HbDumpCopySrcToDest" );
+ // Got a Dump Collect error.. Commit the dumpCollect
+ // errorlog and then send an dump Error mbox message
+ // and FSP will decide what to do.
+ // We do not want dump Collect failures to terminate the
+ // istep.
+ if (l_err)
+ {
+ TRACFCOMP(ISTEPS_TRACE::g_trac_isteps_trace,
+ "ERROR : returned from DUMP::HbDumpCopySrcToDest");
- break;
- }
+ break;
+ }
- } while(0);
+ } while(0);
- DUMP::DUMP_MSG_TYPE msgType = DUMP::DUMP_MSG_END_MSG_TYPE;
+ DUMP::DUMP_MSG_TYPE msgType = DUMP::DUMP_MSG_END_MSG_TYPE;
- // Send dumpCollect success trace
- if (!l_err)
- {
- TRACFCOMP( ISTEPS_TRACE::g_trac_isteps_trace,
- "SUCCESS : doDumpCollect" );
- }
- // got an error that we need to send a ERROR message to FSP
- // and commit the errorlog from dumpCollect.
- else
- {
- msgType = DUMP::DUMP_MSG_ERROR_MSG_TYPE;
+ // Send dumpCollect success trace
+ if (!l_err)
+ {
+ TRACFCOMP( ISTEPS_TRACE::g_trac_isteps_trace,
+ "SUCCESS : doDumpCollect" );
+ }
+ // got an error that we need to send a ERROR message to FSP
+ // and commit the errorlog from dumpCollect.
+ else
+ {
+ msgType = DUMP::DUMP_MSG_ERROR_MSG_TYPE;
- // Commit the dumpCollect errorlog from above as
- // we dont want dump collect to kill the istep
- errlCommit( l_err, HWPF_COMP_ID );
+ // Commit the dumpCollect errorlog from above as
+ // we dont want dump collect to kill the istep
+ errlCommit( l_err, HWPF_COMP_ID );
- }
+ }
- // Send an Error mbox msg to FSP (either end or error)
- l_errMsg = DUMP::sendMboxMsg(msgType);
+ // Send an Error mbox msg to FSP (either end or error)
+ l_errMsg = DUMP::sendMboxMsg(msgType);
- if (l_errMsg)
+ if (l_errMsg)
+ {
+ TRACFCOMP(ISTEPS_TRACE::g_trac_isteps_trace,
+ "ERROR : returned from DUMP::sendMboxMsg" );
+
+ errlCommit( l_errMsg, HWPF_COMP_ID );
+ }
+
+ }
+ else
{
TRACFCOMP(ISTEPS_TRACE::g_trac_isteps_trace,
- "ERROR : returned from DUMP::sendMboxMsg" );
-
- errlCommit( l_errMsg, HWPF_COMP_ID );
+ "ERROR : returned from VFS::module_load (libruntime.so)");
}
-
- }
- else
- {
- TRACFCOMP(ISTEPS_TRACE::g_trac_isteps_trace,
- "ERROR : returned from VFS::module_load (libruntime.so)" );
}
- }
- // If got an error in the procedure or collection of the dump kill the istep
- if( l_err )
- {
- // Create IStep error log and cross reference to error that occurred
- l_StepError.addErrorDetails( l_err );
+ // If got an error in the procedure or collection of the dump kill the
+ // istep
+ if( l_err )
+ {
+ // Create IStep error log and cross reference to error that occurred
+ l_StepError.addErrorDetails( l_err );
- // Commit Error
- errlCommit( l_err, HWPF_COMP_ID );
+ // Commit Error
+ errlCommit( l_err, HWPF_COMP_ID );
+ }
}
TRACFCOMP( ISTEPS_TRACE::g_trac_isteps_trace,
diff --git a/src/usr/isteps/istep14/call_proc_htm_setup.C b/src/usr/isteps/istep14/call_proc_htm_setup.C
index 3feb31e5b..f9bdb861f 100644
--- a/src/usr/isteps/istep14/call_proc_htm_setup.C
+++ b/src/usr/isteps/istep14/call_proc_htm_setup.C
@@ -5,7 +5,7 @@
/* */
/* OpenPOWER HostBoot Project */
/* */
-/* Contributors Listed Below - COPYRIGHT 2015,2016 */
+/* Contributors Listed Below - COPYRIGHT 2015,2018 */
/* [+] International Business Machines Corp. */
/* */
/* */
diff --git a/src/usr/isteps/istep18/establish_system_smp.C b/src/usr/isteps/istep18/establish_system_smp.C
index ab26df229..7430e0d9f 100644
--- a/src/usr/isteps/istep18/establish_system_smp.C
+++ b/src/usr/isteps/istep18/establish_system_smp.C
@@ -427,6 +427,32 @@ errlHndl_t call_host_coalesce_host( )
return l_errl;
}
+static void set_is_master_drawer(TARGETING::EntityPath *master)
+{
+ // Figure out which node we are running on
+ TARGETING::Target *mproc = nullptr;
+ TARGETING::targetService().masterProcChipTargetHandle(mproc);
+
+ TARGETING::EntityPath epath =
+ mproc->getAttr<TARGETING::ATTR_PHYS_PATH>();
+
+ const TARGETING::EntityPath::PathElement pe =
+ epath.pathElementOfType(TARGETING::TYPE_NODE);
+
+ const TARGETING::EntityPath::PathElement mpe =
+ master->pathElementOfType(TARGETING::TYPE_NODE);
+
+ if (pe.instance == mpe.instance)
+ {
+ // Current node is master, set IS_MASTER_DRAWER
+ TARGETING::TargetHandleList l_nodelist;
+ getEncResources(l_nodelist, TARGETING::TYPE_NODE,
+ TARGETING::UTIL_FILTER_FUNCTIONAL);
+ assert(l_nodelist.size() == 1, "ERROR, only looking for one node.");
+ l_nodelist[0]->setAttr<TARGETING::ATTR_IS_MASTER_DRAWER>(1);
+ }
+}
+
//******************************************************************************
// host_sys_fab_iovalid_processing function
//******************************************************************************
@@ -452,6 +478,12 @@ void *host_sys_fab_iovalid_processing(void* io_ptr )
"Master node %s List size = %d bytes Drawer count = %d",
ptr->toString(), drawerData->size, drawerCount);
+ if (drawerCount > 0)
+ {
+ // master node will be first node listed (lowest functional node)
+ set_is_master_drawer(ptr);
+ }
+
// get FABRIC_TO_PHYSICAL_NODE_MAP
TARGETING::Target * sys = NULL;
TARGETING::targetService().getTopLevelTarget( sys );
diff --git a/src/usr/runtime/populate_hbruntime.C b/src/usr/runtime/populate_hbruntime.C
index 43f5f5f15..529f1b3aa 100644
--- a/src/usr/runtime/populate_hbruntime.C
+++ b/src/usr/runtime/populate_hbruntime.C
@@ -2843,16 +2843,6 @@ errlHndl_t populate_hbRuntimeData( void )
do {
TRACFCOMP(g_trac_runtime, "Running populate_hbRuntimeData");
- TARGETING::Target * sys = nullptr;
- TARGETING::targetService().getTopLevelTarget( sys );
- assert(sys != nullptr);
-
- TARGETING::ATTR_HB_EXISTING_IMAGE_type hb_images =
- sys->getAttr<TARGETING::ATTR_HB_EXISTING_IMAGE>();
-
- TRACFCOMP( g_trac_runtime, "ATTR_HB_EXISTING_IMAGE (hb_images) = %x",
- hb_images);
-
// Figure out which node we are running on
TARGETING::Target* mproc = nullptr;
TARGETING::targetService().masterProcChipTargetHandle(mproc);
@@ -2865,13 +2855,23 @@ errlHndl_t populate_hbRuntimeData( void )
uint64_t nodeid = pe.instance;
- TRACFCOMP( g_trac_runtime, "Master node nodid = %x",
- nodeid);
+ TRACFCOMP( g_trac_runtime, "Master node nodeid = %x",
+ nodeid);
// ATTR_HB_EXISTING_IMAGE only gets set on a multi-drawer system.
// Currently set up in host_sys_fab_iovalid_processing() which only
// gets called if there are multiple physical nodes. It eventually
// needs to be setup by a hb routine that snoops for multiple nodes.
+ TARGETING::Target * sys = nullptr;
+ TARGETING::targetService().getTopLevelTarget( sys );
+ assert(sys != nullptr);
+
+ TARGETING::ATTR_HB_EXISTING_IMAGE_type hb_images =
+ sys->getAttr<TARGETING::ATTR_HB_EXISTING_IMAGE>();
+
+ TRACFCOMP( g_trac_runtime, "ATTR_HB_EXISTING_IMAGE (hb_images) = %x",
+ hb_images);
+
if (0 == hb_images) //Single-node
{
if( !TARGETING::is_no_load() )
@@ -2966,7 +2966,7 @@ errlHndl_t populate_hbRuntimeData( void )
TARGETING::ATTR_HB_EXISTING_IMAGE_type mask = 0x1 <<
((sizeof(TARGETING::ATTR_HB_EXISTING_IMAGE_type) * 8) -1);
- TRACFCOMP( g_trac_runtime, "HB_EXISTING_IMAGE (mask) = %#x",
+ TRACFCOMP( g_trac_runtime, "HB_EXISTING_IMAGE (mask) = %x",
mask);
for (uint64_t l_node=0; (l_node < MAX_NODES_PER_SYS); l_node++ )
diff --git a/src/usr/targeting/common/xmltohb/attribute_types_hb.xml b/src/usr/targeting/common/xmltohb/attribute_types_hb.xml
index a28b7e96f..f6aa85a0b 100755
--- a/src/usr/targeting/common/xmltohb/attribute_types_hb.xml
+++ b/src/usr/targeting/common/xmltohb/attribute_types_hb.xml
@@ -5,7 +5,7 @@
<!-- -->
<!-- OpenPOWER HostBoot Project -->
<!-- -->
-<!-- Contributors Listed Below - COPYRIGHT 2012,2017 -->
+<!-- Contributors Listed Below - COPYRIGHT 2012,2018 -->
<!-- [+] Google Inc. -->
<!-- [+] International Business Machines Corp. -->
<!-- -->
@@ -119,6 +119,19 @@
</attribute>
<attribute>
+ <id>IS_MASTER_DRAWER</id>
+ <description>1 = is master node, 0 = is not master node</description>
+ <simpleType>
+ <uint8_t>
+ <default>0</default>
+ </uint8_t>
+ </simpleType>
+ <persistency>volatile-zeroed</persistency>
+ <readable/>
+ <writeable/>
+</attribute>
+
+<attribute>
<id>XSCOM_VIRTUAL_ADDR</id>
<description>Cached Virtual Address of Xscom memory space for this Chip</description>
<simpleType>
diff --git a/src/usr/targeting/common/xmltohb/target_types_hb.xml b/src/usr/targeting/common/xmltohb/target_types_hb.xml
index 60b85e1d6..b62ba346e 100644
--- a/src/usr/targeting/common/xmltohb/target_types_hb.xml
+++ b/src/usr/targeting/common/xmltohb/target_types_hb.xml
@@ -5,7 +5,7 @@
<!-- -->
<!-- OpenPOWER HostBoot Project -->
<!-- -->
-<!-- Contributors Listed Below - COPYRIGHT 2012,2017 -->
+<!-- Contributors Listed Below - COPYRIGHT 2012,2018 -->
<!-- [+] Google Inc. -->
<!-- [+] International Business Machines Corp. -->
<!-- -->
@@ -55,6 +55,7 @@
<attribute><id>PART_NUMBER</id></attribute>
<attribute><id>SERIAL_NUMBER</id></attribute>
<attribute><id>VPD_SWITCHES</id></attribute>
+ <attribute><id>IS_MASTER_DRAWER</id></attribute>
</targetTypeExtension>
<targetTypeExtension>
diff --git a/src/usr/targeting/targplatutil.C b/src/usr/targeting/targplatutil.C
index 5ecad55b9..becdbe40f 100644
--- a/src/usr/targeting/targplatutil.C
+++ b/src/usr/targeting/targplatutil.C
@@ -5,7 +5,7 @@
/* */
/* OpenPOWER HostBoot Project */
/* */
-/* Contributors Listed Below - COPYRIGHT 2013,2017 */
+/* Contributors Listed Below - COPYRIGHT 2013,2018 */
/* [+] International Business Machines Corp. */
/* */
/* */
@@ -40,6 +40,7 @@
// TARG
#include <targeting/targplatutil.H>
#include <targeting/common/predicates/predicates.H>
+#include <targeting/common/utilFilter.H>
#include <errl/errlmanager.H>
#include <config.h>
#include <algorithm>
@@ -110,6 +111,18 @@ void getMasterNodeTarget(Target*& o_masterNodeTarget)
o_masterNodeTarget = masterNodeTarget;
}
+bool isCurrentMasterNode()
+{
+ // Get node target
+ TARGETING::TargetHandleList l_nodelist;
+ getEncResources(l_nodelist, TARGETING::TYPE_NODE,
+ TARGETING::UTIL_FILTER_FUNCTIONAL);
+ assert(l_nodelist.size() == 1, "ERROR, only expect one node.");
+ auto isMaster = l_nodelist[0]->getAttr<TARGETING::ATTR_IS_MASTER_DRAWER>();
+
+ return (isMaster == 1);
+}
+
// return the sensor number from the passed in target
uint32_t getSensorNumber( const TARGETING::Target* i_pTarget,
TARGETING::SENSOR_NAME i_name )
OpenPOWER on IntegriCloud