smbios-mdr: Parse PCIE slot tables

System slot tables have SMBIOS table type 9. Parse system slot tables
and select all PCIE slots. PCIE slot inventory DBus objects will have
the xyz.openbmc_project.Inventory.Item.PCIeSlot interface defined in
phosphor-dbus-interfaces.

Tested:
Tests on a Intel platform. Some DBus command outputs as follows:

busctl introspect xyz.openbmc_project.Smbios.MDR_V2 \
/xyz/openbmc_project/inventory/system/chassis/motherboard/pcieslot3

NAME                                                 TYPE      SIGNATURE RESULT/VALUE                             FLAGS
org.freedesktop.DBus.Introspectable                  interface -         -                                        -herboard/p
.Introspect                                          method    -         s                                        -
org.freedesktop.DBus.Peer                            interface -         -                                        -
.GetMachineId                                        method    -         s                                        -
.Ping                                                method    -         -                                        -
org.freedesktop.DBus.Properties                      interface -         -                                        -
.Get                                                 method    ss        v                                        -
.GetAll                                              method    s         a{sv}                                    -
.Set                                                 method    ssv       -                                        -
.PropertiesChanged                                   signal    sa{sv}as  -                                        -
xyz.openbmc_project.Inventory.Decorator.LocationCode interface -         -                                        -
.LocationCode                                        property  s         "PE3"                                    emits-change writable
xyz.openbmc_project.Inventory.Item                   interface -         -                                        -
.Present                                             property  b         true                                     emits-change writable
.PrettyName                                          property  s         ""                                       emits-change writable
xyz.openbmc_project.Inventory.Item.PCIeSlot          interface -         -                                        -
.Generation                                          property  s         "xyz.openbmc_project.Inventory.Item.P... emits-change writable
.HotPluggable                                        property  b         false                                    emits-change writable
.Lanes                                               property  u         16                                       emits-change writable
.SlotType                                            property  s         "xyz.openbmc_project.Inventory.Item.P... emits-change writable

busctl get-property xyz.openbmc_project.Smbios.MDR_V2 \
/xyz/openbmc_project/inventory/system/chassis/motherboard/pcieslot3 \
xyz.openbmc_project.Inventory.Item.PCIeSlot Generation

s "xyz.openbmc_project.Inventory.Item.PCIeSlot.Generations.Gen5"

busctl get-property xyz.openbmc_project.Smbios.MDR_V2 \
/xyz/openbmc_project/inventory/system/chassis/motherboard/pcieslot3 \
xyz.openbmc_project.Inventory.Item.PCIeSlot SlotType

s "xyz.openbmc_project.Inventory.Item.PCIeSlot.SlotTypes.Unknown"

Note that it shows unknown PCIE slot types, as SMBIOS system slot record
does not have the information that a PCIE slot is full-length,
half-length or low-profile.

Signed-off-by: Jie Yang <jjy@google.com>
Change-Id: Ie9179ceb57dea3f659c15939611e873411de7320
diff --git a/src/mdrv2.cpp b/src/mdrv2.cpp
index 1d173c4..39890b3 100644
--- a/src/mdrv2.cpp
+++ b/src/mdrv2.cpp
@@ -16,6 +16,8 @@
 
 #include "mdrv2.hpp"
 
+#include "pcieslot.hpp"
+
 #include <sys/mman.h>
 
 #include <phosphor-logging/elog-errors.hpp>
@@ -411,6 +413,22 @@
 
 #endif
 
+    pcies.clear();
+    num = getTotalPcieSlot();
+    if (num == -1)
+    {
+        phosphor::logging::log<phosphor::logging::level::ERR>(
+            "get pcie total slot failed");
+        return;
+    }
+
+    for (int index = 0; index < num; index++)
+    {
+        std::string path = pciePath + std::to_string(index);
+        pcies.emplace_back(std::make_unique<phosphor::smbios::Pcie>(
+            bus, path, index, smbiosDir.dir[smbiosDirIndex].dataStorage));
+    }
+
     system.reset();
     system = std::make_unique<System>(
         bus, systemPath, smbiosDir.dir[smbiosDirIndex].dataStorage);
@@ -484,6 +502,47 @@
     return num;
 }
 
+int MDR_V2::getTotalPcieSlot()
+{
+    uint8_t* dataIn = smbiosDir.dir[smbiosDirIndex].dataStorage;
+    int num = 0;
+
+    if (dataIn == nullptr)
+    {
+        phosphor::logging::log<phosphor::logging::level::ERR>(
+            "Fail to get total system slot - no storage data");
+        return -1;
+    }
+
+    while (1)
+    {
+        dataIn = getSMBIOSTypePtr(dataIn, systemSlots);
+        if (dataIn == nullptr)
+        {
+            break;
+        }
+
+        /* System slot type offset. Check if the slot is a PCIE slots. All
+         * PCIE slot type are hardcoded in a table.
+         */
+        if (pcieSmbiosType.find(*(dataIn + 5)) != pcieSmbiosType.end())
+        {
+            num++;
+        }
+        dataIn = smbiosNextPtr(dataIn);
+        if (dataIn == nullptr)
+        {
+            break;
+        }
+        if (num >= limitEntryLen)
+        {
+            break;
+        }
+    }
+
+    return num;
+}
+
 bool MDR_V2::agentSynchronizeData()
 {
     struct MDRSMBIOSHeader mdr2SMBIOS;
diff --git a/src/pcieslot.cpp b/src/pcieslot.cpp
new file mode 100644
index 0000000..1b46407
--- /dev/null
+++ b/src/pcieslot.cpp
@@ -0,0 +1,109 @@
+#include "pcieslot.hpp"
+
+#include <cstdint>
+#include <map>
+
+namespace phosphor
+{
+namespace smbios
+{
+
+void Pcie::pcieInfoUpdate()
+{
+    uint8_t* dataIn = getSMBIOSTypePtr(storage, systemSlots);
+
+    if (dataIn == nullptr)
+    {
+        return;
+    }
+
+    /* offset 5 points to the slot type */
+    for (uint8_t index = 0;
+         index < pcieNum ||
+         pcieSmbiosType.find(*(dataIn + 5)) == pcieSmbiosType.end();)
+    {
+        dataIn = smbiosNextPtr(dataIn);
+        if (dataIn == nullptr)
+        {
+            return;
+        }
+        dataIn = getSMBIOSTypePtr(dataIn, systemSlots);
+        if (dataIn == nullptr)
+        {
+            return;
+        }
+        if (pcieSmbiosType.find(*(dataIn + 5)) != pcieSmbiosType.end())
+        {
+            index++;
+        }
+    }
+
+    auto pcieInfo = reinterpret_cast<struct SystemSlotInfo*>(dataIn);
+
+    pcieGeneration(pcieInfo->slotType);
+    pcieType(pcieInfo->slotType);
+    pcieLaneSize(pcieInfo->slotDataBusWidth);
+    pcieIsHotPluggable(pcieInfo->characteristics2);
+    pcieLocation(pcieInfo->slotDesignation, pcieInfo->length, dataIn);
+
+    /* Pcie slot is embedded on the board. Always be true */
+    Item::present(true);
+}
+
+void Pcie::pcieGeneration(const uint8_t type)
+{
+    std::map<uint8_t, PCIeGeneration>::const_iterator it =
+        pcieGenerationTable.find(type);
+    if (it == pcieGenerationTable.end())
+    {
+        PCIeSlot::generation(PCIeGeneration::Unknown);
+    }
+    else
+    {
+        PCIeSlot::generation(it->second);
+    }
+}
+
+void Pcie::pcieType(const uint8_t type)
+{
+    std::map<uint8_t, PCIeType>::const_iterator it = pcieTypeTable.find(type);
+    if (it == pcieTypeTable.end())
+    {
+        PCIeSlot::slotType(PCIeType::Unknown);
+    }
+    else
+    {
+        PCIeSlot::slotType(it->second);
+    }
+}
+
+void Pcie::pcieLaneSize(const uint8_t width)
+{
+    std::map<uint8_t, size_t>::const_iterator it = pcieLanesTable.find(width);
+    if (it == pcieLanesTable.end())
+    {
+        PCIeSlot::lanes(0);
+    }
+    else
+    {
+        PCIeSlot::lanes(it->second);
+    }
+}
+
+void Pcie::pcieIsHotPluggable(const uint8_t characteristics)
+{
+    /*  Bit 1 of slot characteristics 2 indicates if slot supports hot-plug
+     *  devices
+     */
+    PCIeSlot::hotPluggable(characteristics & 0x2);
+}
+
+void Pcie::pcieLocation(const uint8_t slotDesignation, const uint8_t structLen,
+                        uint8_t* dataIn)
+{
+    location::locationCode(
+        positionToString(slotDesignation, structLen, dataIn));
+}
+
+} // namespace smbios
+} // namespace phosphor