Proper support for initializing the isolator

Signed-off-by: Zane Shelley <zshelle@us.ibm.com>
Change-Id: Id9a65dc6318420144a7ba9fb78d8a317f1181917
diff --git a/analyzer/analyzer_main.cpp b/analyzer/analyzer_main.cpp
index 774fb0a..cf3d195 100644
--- a/analyzer/analyzer_main.cpp
+++ b/analyzer/analyzer_main.cpp
@@ -4,6 +4,7 @@
 
 #include <hei_main.hpp>
 #include <phosphor-logging/log.hpp>
+#include <util/pdbg.hpp>
 #include <util/trace.hpp>
 
 #include <algorithm>
@@ -15,50 +16,11 @@
 namespace analyzer
 {
 
-/**
- * @brief send chip data file to isolator
- *
- * Read a chip data file into memory and then send it to the isolator via
- * the initialize interface.
- *
- * @param i_filePath The file path and name to read into memory
- *
- * @return Returns true if the isolator was successfully initialized with
- *         a single chip data file. Returns false otherwise.
- *
- */
-void initWithFile(const char* i_filePath)
-{
-    // open the file and seek to the end to get length
-    std::ifstream fileStream(i_filePath, std::ios::binary | std::ios::ate);
+//------------------------------------------------------------------------------
 
-    if (!fileStream.good())
-    {
-        trace::err("Unable to open file: %s", i_filePath);
-        assert(0);
-    }
-    else
-    {
-        // get file size based on seek position
-        fileStream.seekg(0, std::ios::end);
-        std::ifstream::pos_type fileSize = fileStream.tellg();
+// Forward references for externally defined functions.
 
-        // create a buffer large enough to hold the entire file
-        std::vector<char> fileBuffer(fileSize);
-
-        // seek to the beginning of the file
-        fileStream.seekg(0, std::ios::beg);
-
-        // read the entire file into the buffer
-        fileStream.read(fileBuffer.data(), fileSize);
-
-        // done with the file
-        fileStream.close();
-
-        // initialize the isolator with the chip data
-        libhei::initialize(fileBuffer.data(), fileSize);
-    }
-}
+void initializeIsolator(const std::vector<libhei::Chip>& i_chips);
 
 //------------------------------------------------------------------------------
 
@@ -78,11 +40,6 @@
 
 //------------------------------------------------------------------------------
 
-const char* __path(const libhei::Chip& i_chip)
-{
-    return pdbg_target_path((pdbg_target*)i_chip.getChip());
-}
-
 const char* __attn(libhei::AttentionType_t i_attnType)
 {
     const char* str = "";
@@ -130,10 +87,8 @@
 
 //------------------------------------------------------------------------------
 
-// Returns the chip model/level of the given target. Also, adds the chip
-// model/level to the list of type types needed to initialize the isolator.
-libhei::ChipType_t __getChipType(pdbg_target* i_trgt,
-                                 std::vector<libhei::ChipType_t>& o_types)
+// Returns the chip model/level of the given target.
+libhei::ChipType_t __getChipType(pdbg_target* i_trgt)
 {
     libhei::ChipType_t type;
 
@@ -158,23 +113,13 @@
     }
     // END WORKAROUND
 
-    // Make sure the model/level list contains unique values only.
-    // This is O(n*n), but the list size will likely be very low, probably
-    // maxing around a half dozen. So, opting for simplicity.
-    if (o_types.end() == std::find(o_types.begin(), o_types.end(), type))
-    {
-        o_types.push_back(type);
-    }
-
     return type;
 }
 
 //------------------------------------------------------------------------------
 
-// Gathers list of active chips to analyze. Also, returns the list of chip types
-// needed to initialize the isolator.
-void __getActiveChips(std::vector<libhei::Chip>& o_chips,
-                      std::vector<libhei::ChipType_t>& o_types)
+// Gathers list of active chips to analyze.
+void __getActiveChips(std::vector<libhei::Chip>& o_chips)
 {
     // Iterate each processor.
     pdbg_target* procTrgt;
@@ -185,7 +130,7 @@
             continue;
 
         // Add the processor to the list.
-        o_chips.emplace_back(procTrgt, __getChipType(procTrgt, o_types));
+        o_chips.emplace_back(procTrgt, __getChipType(procTrgt));
 
         // Iterate the connected OCMBs, if they exist.
         pdbg_target* ocmbTrgt;
@@ -196,46 +141,9 @@
                 continue;
 
             // Add the OCMB to the list.
-            o_chips.emplace_back(ocmbTrgt, __getChipType(ocmbTrgt, o_types));
+            o_chips.emplace_back(ocmbTrgt, __getChipType(ocmbTrgt));
         }
     }
-
-    // For debug, trace out all of the chips found.
-    for (const auto& chip : o_chips)
-    {
-        trace::inf("chip:%s type:0x%0" PRIx32, __path(chip), chip.getType());
-    }
-}
-
-//------------------------------------------------------------------------------
-
-// Initializes the isolator for each specified chip type.
-void __initializeIsolator(const std::vector<libhei::ChipType_t>& i_types)
-{
-    // START WORKAROUND
-    // TODO: The chip data will eventually come from the CHIPDATA section of the
-    //       PNOR. Until that support is available, we'll use temporary chip
-    //       data files.
-    for (const auto& type : i_types)
-    {
-        switch (type)
-        {
-            case 0x120DA049: // PROC
-                initWithFile(
-                    "/usr/share/openpower-hw-diags/chip_data_proc.cdb");
-                break;
-
-            case 0x160D2000: // OCMB_CHIP
-                initWithFile(
-                    "/usr/share/openpower-hw-diags/chip_data_ocmb.cdb");
-                break;
-
-            default:
-                trace::err("Unsupported ChipType_t value: 0x%0" PRIx32, type);
-                assert(0);
-        }
-    }
-    // END WORKAROUND
 }
 
 //------------------------------------------------------------------------------
@@ -248,8 +156,9 @@
     // For debug, trace out the original list of signatures before filtering.
     for (const auto& sig : io_list)
     {
-        trace::inf("Signature: %s 0x%0" PRIx32 " %s", __path(sig.getChip()),
-                   __sig(sig), __attn(sig.getAttnType()));
+        trace::inf("Signature: %s 0x%0" PRIx32 " %s",
+                   util::pdbg::getPath(sig.getChip()), __sig(sig),
+                   __attn(sig.getAttnType()));
     }
 
     // Special and host attentions are not supported by this user application.
@@ -309,7 +218,8 @@
         word8 = __sig(root);
 
         trace::inf("Root cause attention: %s 0x%0" PRIx32 " %s",
-                   __path(root.getChip()), word8, __attn(root.getAttnType()));
+                   util::pdbg::getPath(root.getChip()), word8,
+                   __attn(root.getAttnType()));
     }
 
     // Get the log data.
@@ -345,19 +255,18 @@
 
     trace::inf(">>> enter analyzeHardware()");
 
-    // Get the active chips to be analyzed and their types.
-    std::vector<libhei::Chip> chipList;
-    std::vector<libhei::ChipType_t> chipTypes;
-    __getActiveChips(chipList, chipTypes);
+    // Get the active chips to be analyzed.
+    std::vector<libhei::Chip> chips;
+    __getActiveChips(chips);
 
-    // Initialize the isolator for all chip types.
-    trace::inf("Initializing isolator: # of types=%u", chipTypes.size());
-    __initializeIsolator(chipTypes);
+    // Initialize the isolator for all chips.
+    trace::inf("Initializing the isolator...");
+    initializeIsolator(chips);
 
     // Isolate attentions.
-    trace::inf("Isolating errors: # of chips=%u", chipList.size());
+    trace::inf("Isolating errors: # of chips=%u", chips.size());
     libhei::IsolationData isoData{};
-    libhei::isolate(chipList, isoData);
+    libhei::isolate(chips, isoData);
 
     // Filter signatures to determine root cause. We'll need to make a copy of
     // the list so that the original list is maintained for the log.
@@ -368,7 +277,7 @@
     attnFound = __logError(sigList, isoData);
 
     // All done, clean up the isolator.
-    trace::inf("Uninitializing isolator");
+    trace::inf("Uninitializing isolator...");
     libhei::uninitialize();
 
     trace::inf("<<< exit analyzeHardware()");
diff --git a/analyzer/initialize_isolator.cpp b/analyzer/initialize_isolator.cpp
new file mode 100644
index 0000000..941b500
--- /dev/null
+++ b/analyzer/initialize_isolator.cpp
@@ -0,0 +1,157 @@
+
+#include <assert.h>
+
+#include <hei_main.hpp>
+#include <util/pdbg.hpp>
+#include <util/trace.hpp>
+
+#include <filesystem>
+#include <fstream>
+#include <map>
+#include <vector>
+
+namespace fs = std::filesystem;
+
+namespace analyzer
+{
+
+//------------------------------------------------------------------------------
+
+void __getChipDataFiles(std::map<libhei::ChipType_t, fs::path>& o_files)
+{
+    o_files.clear();
+
+    auto directory = "/usr/share/openpower-libhei/";
+
+    for (const auto& entry : fs::directory_iterator(directory))
+    {
+        auto path = entry.path();
+
+        std::ifstream file{path, std::ios::binary};
+        if (!file.good())
+        {
+            trace::err("Unable to open file: %s", path.string().c_str());
+            continue;
+        }
+
+        // The first 8-bytes is the file keyword and the next 4-bytes is the
+        // chip type.
+        libhei::FileKeyword_t keyword;
+        libhei::ChipType_t chipType;
+
+        const size_t sz_keyword  = sizeof(keyword);
+        const size_t sz_chipType = sizeof(chipType);
+        const size_t sz_buffer   = sz_keyword + sz_chipType;
+
+        // Read the keyword and chip type from the file.
+        char buffer[sz_buffer];
+        file.read(buffer, sz_buffer);
+        if (!file.good())
+        {
+            trace::err("Unable to read file: %s", path.string().c_str());
+            continue;
+        }
+
+        // Get the keyword.
+        memcpy(&keyword, &buffer[0], sz_keyword);
+        keyword = be64toh(keyword);
+
+        // Ensure the keyword value is correct.
+        if (libhei::KW_CHIPDATA != keyword)
+        {
+            trace::err("Invalid chip data file: %s", path.string().c_str());
+            continue;
+        }
+
+        // Get the chip type.
+        memcpy(&chipType, &buffer[sz_keyword], sz_chipType);
+        chipType = be32toh(chipType);
+
+        // Trace each legitimate chip data file for debug.
+        trace::inf("File found: type=0x%0" PRIx32 " path=%s", chipType,
+                   path.string().c_str());
+
+        // So far, so good. Add the entry.
+        auto ret = o_files.emplace(chipType, path);
+        assert(ret.second); // Should not have duplicate entries
+    }
+}
+
+//------------------------------------------------------------------------------
+
+void __initialize(const fs::path& i_path)
+{
+    // Get file size.
+    const auto sz_buffer = fs::file_size(i_path);
+
+    // Create a buffer large enough to hold the entire file.
+    std::vector<char> buffer(sz_buffer);
+
+    // Open the chip data file.
+    std::ifstream file{i_path, std::ios::binary};
+    assert(file.good()); // We've opened it once before, so it should open now.
+
+    // Read the entire file into the buffer.
+    file.read(buffer.data(), sz_buffer);
+    assert(file.good()); // Again, this should be readable.
+
+    // This is not necessary, but it frees up memory before calling the memory
+    // intensive initialize() function.
+    file.close();
+
+    // Initialize the isolator with this chip data file.
+    libhei::initialize(buffer.data(), sz_buffer);
+}
+
+//------------------------------------------------------------------------------
+
+void initializeIsolator(const std::vector<libhei::Chip>& i_chips)
+{
+    // Find all of the existing chip data files.
+    std::map<libhei::ChipType_t, fs::path> files;
+    __getChipDataFiles(files);
+
+    // Keep track of models/levels that have already been initialized.
+    std::map<libhei::ChipType_t, unsigned int> initTypes;
+
+    for (const auto& chip : i_chips)
+    {
+        auto chipType = chip.getType();
+
+        // Trace each chip for debug.
+        trace::inf("Chip found: type=0x%0" PRIx32 " chip=%s", chipType,
+                   util::pdbg::getPath(chip));
+
+        if (0 == chipType)
+        {
+            // This is a special case. It means the model/level attributes have
+            // not been initialized in the devtree. This is possible on the
+            // epoch IPL where an attention occurs before Hostboot is able to
+            // update the devtree information on the BMC.
+
+            // TODO: Consider logging a PEL. Just skip for now.
+            continue;
+        }
+
+        // Mark this chip type as initialized (or will be if it hasn't been).
+        auto ret = initTypes.emplace(chipType, 1);
+        if (!ret.second)
+        {
+            // This type has already been initialized. Nothing more to do.
+            continue;
+        }
+
+        // Get the file for this chip.
+        auto itr = files.find(chipType);
+
+        // Ensure a chip data file exist for this chip.
+        assert(files.end() != itr);
+
+        // Initialize this chip type.
+        __initialize(itr->second);
+    }
+}
+
+//------------------------------------------------------------------------------
+
+} // namespace analyzer
diff --git a/analyzer/meson.build b/analyzer/meson.build
index 191e9aa..40a65d3 100644
--- a/analyzer/meson.build
+++ b/analyzer/meson.build
@@ -2,7 +2,9 @@
 analyzer_src = files(
     'analyzer_main.cpp',
     'hei_user_interface.cpp',
+    'initialize_isolator.cpp',
     '../util/ffdc_file.cpp',
+    '../util/pdbg.cpp',
     '../util/temporary_file.cpp',
 )
 
diff --git a/util/pdbg.cpp b/util/pdbg.cpp
new file mode 100644
index 0000000..14768c0
--- /dev/null
+++ b/util/pdbg.cpp
@@ -0,0 +1,23 @@
+
+#include <libpdbg.h>
+
+#include <util/pdbg.hpp>
+
+namespace util
+{
+
+namespace pdbg
+{
+
+//------------------------------------------------------------------------------
+
+const char* getPath(const libhei::Chip& i_chip)
+{
+    return pdbg_target_path((pdbg_target*)i_chip.getChip());
+}
+
+//------------------------------------------------------------------------------
+
+} // namespace pdbg
+
+} // namespace util
diff --git a/util/pdbg.hpp b/util/pdbg.hpp
new file mode 100644
index 0000000..2ee1c61
--- /dev/null
+++ b/util/pdbg.hpp
@@ -0,0 +1,19 @@
+#pragma once
+
+#include <hei_main.hpp>
+
+namespace util
+{
+
+namespace pdbg
+{
+
+/**
+ * @param  A chip.
+ * @return A string representing the chip's devtree path.
+ */
+const char* getPath(const libhei::Chip& i_chip);
+
+} // namespace pdbg
+
+} // namespace util