PEL: Generate HW callouts from the msg registry

Callouts that aren't procedures or symbolic FRUs can be defined in the
message registry.  Fill in the SRC code that creates these hardware
callout objects in this case.

Signed-off-by: Matt Spinler <spinler@us.ibm.com>
Change-Id: I610c6c58b8672da283722c1d68b6dfdf28e480e3
diff --git a/extensions/openpower-pels/src.cpp b/extensions/openpower-pels/src.cpp
index d2e58d5..eb103bb 100644
--- a/extensions/openpower-pels/src.cpp
+++ b/extensions/openpower-pels/src.cpp
@@ -510,7 +510,7 @@
     auto item = additionalData.getValue("CALLOUT_INVENTORY_PATH");
     if (item)
     {
-        addInventoryCallout(*item, dataIface);
+        addInventoryCallout(*item, std::nullopt, std::nullopt, dataIface);
     }
 
     // TODO: CALLOUT_DEVICE_PATH
@@ -522,6 +522,8 @@
 }
 
 void SRC::addInventoryCallout(const std::string& inventoryPath,
+                              const std::optional<CalloutPriority>& priority,
+                              const std::optional<std::string>& locationCode,
                               const DataInterfaceBase& dataIface)
 {
     std::string locCode;
@@ -532,14 +534,24 @@
 
     try
     {
-        locCode = dataIface.getLocationCode(inventoryPath);
+        // Use the passed in location code if there otherwise look it up
+        if (locationCode)
+        {
+            locCode = *locationCode;
+        }
+        else
+        {
+            locCode = dataIface.getLocationCode(inventoryPath);
+        }
 
         try
         {
             dataIface.getHWCalloutFields(inventoryPath, fn, ccin, sn);
 
-            callout = std::make_unique<src::Callout>(CalloutPriority::high,
-                                                     locCode, fn, ccin, sn);
+            CalloutPriority p =
+                priority ? priority.value() : CalloutPriority::high;
+
+            callout = std::make_unique<src::Callout>(p, locCode, fn, ccin, sn);
         }
         catch (const SdBusError& e)
         {
@@ -596,10 +608,23 @@
                              const DataInterfaceBase& dataIface)
 {
     std::unique_ptr<src::Callout> callout;
-
-    // TODO: expand this location code.
     auto locCode = regCallout.locCode;
 
+    if (!locCode.empty())
+    {
+        try
+        {
+            locCode = dataIface.expandLocationCode(locCode, 0);
+        }
+        catch (const std::exception& e)
+        {
+            auto msg =
+                "Unable to expand location code " + locCode + ": " + e.what();
+            addDebugData(msg);
+            return;
+        }
+    }
+
     // Via the PEL values table, get the priority enum.
     // The schema will have validated the priority was a valid value.
     auto priorityIt =
@@ -631,7 +656,25 @@
     }
     else
     {
-        // TODO: HW callouts
+        // A hardware callout
+        std::string inventoryPath;
+
+        try
+        {
+            // Get the inventory item from the unexpanded location code
+            inventoryPath =
+                dataIface.getInventoryFromLocCode(regCallout.locCode, 0);
+        }
+        catch (const std::exception& e)
+        {
+            std::string msg =
+                "Unable to get inventory path from location code: " + locCode +
+                ": " + e.what();
+            addDebugData(msg);
+            return;
+        }
+
+        addInventoryCallout(inventoryPath, priority, locCode, dataIface);
     }
 
     if (callout)
diff --git a/extensions/openpower-pels/src.hpp b/extensions/openpower-pels/src.hpp
index 52a973c..cc9b1c7 100644
--- a/extensions/openpower-pels/src.hpp
+++ b/extensions/openpower-pels/src.hpp
@@ -357,9 +357,13 @@
      * @brief Adds a FRU callout based on an inventory path
      *
      * @param[in] inventoryPath - The inventory item to call out
+     * @param[in] priority - An optional priority (uses high if nullopt)
+     * @param[in] locationCode - The expanded location code (or look it up)
      * @param[in] dataIface - The DataInterface object
      */
     void addInventoryCallout(const std::string& inventoryPath,
+                             const std::optional<CalloutPriority>& priority,
+                             const std::optional<std::string>& locationCode,
                              const DataInterfaceBase& dataIface);
 
     /**
diff --git a/test/openpower-pels/src_test.cpp b/test/openpower-pels/src_test.cpp
index d767c53..5c5c868 100644
--- a/test/openpower-pels/src_test.cpp
+++ b/test/openpower-pels/src_test.cpp
@@ -510,7 +510,20 @@
                     "SymbolicFRUTrusted": "service_docs"
                 }
             ]
-
+        },
+        {
+            "System": "systemC",
+            "CalloutList":
+            [
+                {
+                    "Priority": "high",
+                    "LocCode": "P0-C8"
+                },
+                {
+                    "Priority": "medium",
+                    "LocCode": "P0-C9"
+                }
+            ]
         }
         ])"_json;
 
@@ -556,6 +569,7 @@
         NiceMock<MockDataInterface> dataIface;
         std::vector<std::string> names{"systemB"};
 
+        EXPECT_CALL(dataIface, expandLocationCode).WillOnce(Return("P0-C8"));
         EXPECT_CALL(dataIface, getSystemNames).WillOnce(ReturnRef(names));
 
         SRC src{entry, ad, dataIface};
@@ -586,4 +600,68 @@
         EXPECT_FALSE(fru2->getSN());
         EXPECT_FALSE(fru2->getCCIN());
     }
+
+    {
+        // Two hardware callouts
+        AdditionalData ad;
+        NiceMock<MockDataInterface> dataIface;
+        std::vector<std::string> names{"systemC"};
+
+        EXPECT_CALL(dataIface, getSystemNames).WillOnce(ReturnRef(names));
+
+        EXPECT_CALL(dataIface, expandLocationCode("P0-C8", 0))
+            .WillOnce(Return("UXXX-P0-C8"));
+
+        EXPECT_CALL(dataIface, expandLocationCode("P0-C9", 0))
+            .WillOnce(Return("UXXX-P0-C9"));
+
+        EXPECT_CALL(dataIface, getInventoryFromLocCode("P0-C8", 0))
+            .WillOnce(Return(
+                "/xyz/openbmc_project/inventory/chassis/motherboard/cpu0"));
+
+        EXPECT_CALL(dataIface, getInventoryFromLocCode("P0-C9", 0))
+            .WillOnce(Return(
+                "/xyz/openbmc_project/inventory/chassis/motherboard/cpu1"));
+
+        EXPECT_CALL(
+            dataIface,
+            getHWCalloutFields(
+                "/xyz/openbmc_project/inventory/chassis/motherboard/cpu0", _, _,
+                _))
+            .Times(1)
+            .WillOnce(DoAll(SetArgReferee<1>("1234567"),
+                            SetArgReferee<2>("CCCC"),
+                            SetArgReferee<3>("123456789ABC")));
+
+        EXPECT_CALL(
+            dataIface,
+            getHWCalloutFields(
+                "/xyz/openbmc_project/inventory/chassis/motherboard/cpu1", _, _,
+                _))
+            .Times(1)
+            .WillOnce(DoAll(SetArgReferee<1>("2345678"),
+                            SetArgReferee<2>("DDDD"),
+                            SetArgReferee<3>("23456789ABCD")));
+
+        SRC src{entry, ad, dataIface};
+
+        auto& callouts = src.callouts()->callouts();
+        EXPECT_EQ(callouts.size(), 2);
+
+        EXPECT_EQ(callouts[0]->locationCode(), "UXXX-P0-C8");
+        EXPECT_EQ(callouts[0]->priority(), 'H');
+
+        auto& fru1 = callouts[0]->fruIdentity();
+        EXPECT_EQ(fru1->getPN().value(), "1234567");
+        EXPECT_EQ(fru1->getCCIN().value(), "CCCC");
+        EXPECT_EQ(fru1->getSN().value(), "123456789ABC");
+
+        EXPECT_EQ(callouts[1]->locationCode(), "UXXX-P0-C9");
+        EXPECT_EQ(callouts[1]->priority(), 'M');
+
+        auto& fru2 = callouts[1]->fruIdentity();
+        EXPECT_EQ(fru2->getPN().value(), "2345678");
+        EXPECT_EQ(fru2->getCCIN().value(), "DDDD");
+        EXPECT_EQ(fru2->getSN().value(), "23456789ABCD");
+    }
 }