Add CPU dbus service for MDR V2

Add all CPU information in smbios table and provide dbus
interface for redfish to get CPU information.

Tested:
DC cycle system and waiting for BIOS entering setup page.
Check CPU information in Redfish, Redfish should show correct CPU
information.

Signed-off-by: Cheng C Yang <cheng.c.yang@linux.intel.com>
Change-Id: I8e6f803c516267de094a01fb1b1fa7d2420d2474
diff --git a/include/cpu.hpp b/include/cpu.hpp
new file mode 100644
index 0000000..78c4c9c
--- /dev/null
+++ b/include/cpu.hpp
@@ -0,0 +1,197 @@
+/*
+// Copyright (c) 2018 Intel Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+*/
+
+#pragma once
+#include "smbios_mdrv2.hpp"
+
+#include <xyz/openbmc_project/Inventory/Decorator/Asset/server.hpp>
+#include <xyz/openbmc_project/Inventory/Item/Cpu/server.hpp>
+#include <xyz/openbmc_project/Inventory/Item/server.hpp>
+#include <xyz/openbmc_project/State/Decorator/OperationalStatus/server.hpp>
+
+namespace phosphor
+{
+
+namespace smbios
+{
+
+// Definition follow smbios spec DSP0134 3.0.0
+static const std::map<uint8_t, const char *> processorTypeTable = {
+    {0x1, "Other"},          {0x2, "Unknown"},       {0x3, "Central Processor"},
+    {0x4, "Math Processor"}, {0x5, "DSP Processor"}, {0x6, "Video Processor"},
+};
+
+// Definition follow smbios spec DSP0134 3.0.0
+static const std::map<uint8_t, const char *> familyTable = {
+    {0x1, "Other"},
+    {0x2, "Unknown"},
+    {0x10, "Pentium II Xeon processor"},
+    {0xa1, "Quad-Core Intel Xeon processor 3200 Series"},
+    {0xa2, "Dual-Core Intel Xeon processor 3000 Series"},
+    {0xa3, "Quad-Core Intel Xeon processor 5300 Series"},
+    {0xa4, "Dual-Core Intel Xeon processor 5100 Series"},
+    {0xa5, "Dual-Core Intel Xeon processor 5000 Series"},
+    {0xa6, "Dual-Core Intel Xeon processor LV"},
+    {0xa7, "Dual-Core Intel Xeon processor ULV"},
+    {0xa8, "Dual-Core Intel Xeon processor 7100 Series"},
+    {0xa9, "Quad-Core Intel Xeon processor 5400 Series"},
+    {0xaa, "Quad-Core Intel Xeon processor"},
+    {0xab, "Dual-Core Intel Xeon processor 5200 Series"},
+    {0xac, "Dual-Core Intel Xeon processor 7200 Series"},
+    {0xad, "Quad-Core Intel Xeon processor 7300 Series"},
+    {0xae, "Quad-Core Intel Xeon processor 7400 Series"},
+    {0xaf, "Multi-Core Intel Xeon processor 7400 Series"},
+    {0xb0, "Pentium III Xeon processor"},
+    {0xb3, "Intel Xeon processor"},
+    {0xb5, "Intel Xeon processor MP"},
+    {0xd6, "Multi-Core Intel Xeon processor"},
+    {0xd7, "Dual-Core Intel Xeon processor 3xxx Series"},
+    {0xd8, "Quad-Core Intel Xeon processor 3xxx Series"},
+    {0xd9, "VIA Nano Processor Family"},
+    {0xda, "Dual-Core Intel Xeon processor 5xxx Series"},
+    {0xdb, "Quad-Core Intel Xeon processor 5xxx Series"},
+    {0xdd, "Dual-Core Intel Xeon processor 7xxx Series"},
+    {0xde, "Quad-Core Intel Xeon processor 7xxx Series"},
+    {0xdf, "Multi-Core Intel Xeon processor 7xxx Series"},
+    {0xe0, "Multi-Core Intel Xeon processor 3400 Series"}
+
+};
+
+// Definition follow smbios spec DSP0134 3.0.0
+static const std::array<std::string, 16> characteristicsTable{
+    "Reserved",
+    "Unknown",
+    "64-bit Capable",
+    "Multi-Core",
+    "Hardware Thread",
+    "Execute Protection",
+    "Enhanced Virtualization",
+    "Power/Performance Control",
+    "Reserved",
+    "Reserved",
+    "Reserved",
+    "Reserved",
+    "Reserved",
+    "Reserved",
+    "Reserved",
+    "Reserved"};
+
+class Cpu
+    : sdbusplus::server::object::object<
+          sdbusplus::xyz::openbmc_project::Inventory::Item::server::Cpu>,
+      sdbusplus::server::object::object<
+          sdbusplus::xyz::openbmc_project::Inventory::server::Item>,
+      sdbusplus::server::object::object<
+          sdbusplus::xyz::openbmc_project::Inventory::Decorator::server::Asset>,
+      sdbusplus::server::object::object<
+          sdbusplus::xyz::openbmc_project::State::Decorator::server::
+              OperationalStatus>
+{
+  public:
+    Cpu() = delete;
+    Cpu(const Cpu &) = delete;
+    Cpu &operator=(const Cpu &) = delete;
+    Cpu(Cpu &&) = delete;
+    Cpu &operator=(Cpu &&) = delete;
+    ~Cpu() = default;
+
+    Cpu(sdbusplus::bus::bus &bus, const std::string &objPath,
+        const uint8_t &cpuId, uint8_t *smbiosTableStorage) :
+        sdbusplus::server::object::object<
+            sdbusplus::xyz::openbmc_project::Inventory::Item::server::Cpu>(
+            bus, objPath.c_str()),
+        sdbusplus::server::object::object<
+            sdbusplus::xyz::openbmc_project::Inventory::Decorator::server::
+                Asset>(bus, objPath.c_str()),
+        sdbusplus::server::object::object<
+            sdbusplus::xyz::openbmc_project::Inventory::server::Item>(
+            bus, objPath.c_str()),
+        sdbusplus::server::object::object<
+            sdbusplus::xyz::openbmc_project::State::Decorator::server::
+                OperationalStatus>(bus, objPath.c_str()),
+        cpuNum(cpuId), storage(smbiosTableStorage)
+    {
+        processorInfoUpdate();
+    }
+
+    void processorInfoUpdate(void);
+
+    std::string processorSocket(std::string value) override;
+    std::string processorType(std::string value) override;
+    std::string processorFamily(std::string value) override;
+    std::string manufacturer(std::string value) override;
+    uint32_t processorId(uint32_t value) override;
+    std::string processorVersion(std::string value) override;
+    uint16_t processorMaxSpeed(uint16_t value) override;
+    std::string processorCharacteristics(std::string value) override;
+    uint16_t processorCoreCount(uint16_t value) override;
+    uint16_t processorThreadCount(uint16_t value) override;
+    bool present(bool value) override;
+    bool functional(bool value) override;
+
+  private:
+    uint8_t cpuNum;
+
+    uint8_t *storage;
+
+    struct ProcessorInfo
+    {
+        uint8_t type;
+        uint8_t length;
+        uint16_t handle;
+        uint8_t socketDesignation;
+        uint8_t processorType;
+        uint8_t family;
+        uint8_t manufacturer;
+        uint64_t id;
+        uint8_t version;
+        uint8_t voltage;
+        uint16_t exClock;
+        uint16_t maxSpeed;
+        uint16_t currSpeed;
+        uint8_t status;
+        uint8_t upgrade;
+        uint16_t l1Handle;
+        uint16_t l2Handle;
+        uint16_t l3Handle;
+        uint8_t serialNum;
+        uint8_t assetTag;
+        uint8_t partNum;
+        uint8_t coreCount;
+        uint8_t coreEnable;
+        uint8_t threadCount;
+        uint16_t characteristics;
+        uint16_t family2;
+        uint16_t coreCount2;
+        uint16_t coreEnable2;
+        uint16_t threadCount2;
+    } __attribute__((packed));
+
+    void cpuSocket(const uint8_t positionNum, const uint8_t structLen,
+                   uint8_t *dataIn);
+    void cpuType(const uint8_t value);
+    void cpuFamily(const uint8_t value);
+    void cpuManufacturer(const uint8_t positionNum, const uint8_t structLen,
+                         uint8_t *dataIn);
+    void cpuVersion(const uint8_t positionNum, const uint8_t structLen,
+                    uint8_t *dataIn);
+    void cpuCharacteristics(const uint16_t value);
+    void cpuStatus(const uint8_t value);
+};
+
+} // namespace smbios
+
+} // namespace phosphor
diff --git a/include/mdrv2.hpp b/include/mdrv2.hpp
index 465f0ee..363a004 100644
--- a/include/mdrv2.hpp
+++ b/include/mdrv2.hpp
@@ -15,6 +15,7 @@
 */
 
 #pragma once
+#include "cpu.hpp"
 
 #include <sys/stat.h>
 #include <sys/types.h>
@@ -27,6 +28,7 @@
 #include <smbios.hpp>
 #include <xyz/openbmc_project/Smbios/MDR_V2/server.hpp>
 
+static constexpr int limitEntryLen = 0xff;
 static constexpr uint8_t mdr2Version = 2;
 static constexpr uint32_t mdr2SMSize = 0x00100000;
 static constexpr uint32_t mdr2SMBaseAddress = 0x9FF00000;
@@ -169,6 +171,9 @@
     bool smbiosIsAvailForUpdate(uint8_t index);
     inline uint8_t smbiosValidFlag(uint8_t index);
     void systemInfoUpdate(void);
+
+    int getTotalCpuSlot(void);
+    std::vector<std::unique_ptr<Cpu>> cpus;
 };
 
 } // namespace smbios
diff --git a/include/smbios.hpp b/include/smbios.hpp
index 71140de..2e02d82 100644
--- a/include/smbios.hpp
+++ b/include/smbios.hpp
@@ -67,3 +67,88 @@
     physicalMemoryArrayType = 16,
     memoryDeviceType = 17,
 } SmbiosType;
+
+static constexpr uint8_t separateLen = 2;
+
+static inline uint8_t* smbiosNextPtr(uint8_t* smbiosDataIn)
+{
+    if (smbiosDataIn == nullptr)
+    {
+        return nullptr;
+    }
+    uint8_t* smbiosData = smbiosDataIn + *(smbiosDataIn + 1);
+    int len = 0;
+    while ((*smbiosData | *(smbiosData + 1)) != 0)
+    {
+        smbiosData++;
+        len++;
+        if (len >= mdrSMBIOSSize) // To avoid endless loop
+        {
+            return nullptr;
+        }
+    }
+    return smbiosData + separateLen;
+}
+
+// When first time run getSMBIOSTypePtr, need to send the RegionS[].regionData
+// to smbiosDataIn
+static inline uint8_t* getSMBIOSTypePtr(uint8_t* smbiosDataIn, uint8_t typeId)
+{
+    if (smbiosDataIn == nullptr)
+    {
+        return nullptr;
+    }
+    char* smbiosData = reinterpret_cast<char*>(smbiosDataIn);
+    while ((*smbiosData != '\0') || (*(smbiosData + 1) != '\0'))
+    {
+        if (*smbiosData != typeId)
+        {
+            uint32_t len = *(smbiosData + 1);
+            smbiosData += len;
+            while ((*smbiosData != '\0') || (*(smbiosData + 1) != '\0'))
+            {
+                smbiosData++;
+                len++;
+                if (len >= mdrSMBIOSSize) // To avoid endless loop
+                {
+                    return nullptr;
+                }
+            }
+            smbiosData += separateLen;
+            continue;
+        }
+        return reinterpret_cast<uint8_t*>(smbiosData);
+    }
+    return nullptr;
+}
+
+static inline std::string positionToString(uint8_t positionNum,
+                                           uint8_t structLen, uint8_t* dataIn)
+{
+    if (dataIn == nullptr)
+    {
+        return "";
+    }
+    uint16_t limit = mdrSMBIOSSize; // set a limit to avoid endless loop
+
+    char* target = reinterpret_cast<char*>(dataIn + structLen);
+    for (uint8_t index = 1; index < positionNum; index++)
+    {
+        for (; *target != '\0'; target++)
+        {
+            limit--;
+            if (limit < 1)
+            {
+                return "";
+            }
+        }
+        target++;
+        if (*target == '\0')
+        {
+            return ""; // 0x00 0x00 means end of the entry.
+        }
+    }
+
+    std::string result = target;
+    return result;
+}
diff --git a/src/cpu.cpp b/src/cpu.cpp
new file mode 100644
index 0000000..f7681c9
--- /dev/null
+++ b/src/cpu.cpp
@@ -0,0 +1,249 @@
+/*
+// Copyright (c) 2018 Intel Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+*/
+
+#include "cpu.hpp"
+
+#include <iostream>
+#include <map>
+
+namespace phosphor
+{
+namespace smbios
+{
+
+void Cpu::cpuSocket(const uint8_t positionNum, const uint8_t structLen,
+                    uint8_t *dataIn)
+{
+    std::string result = positionToString(positionNum, structLen, dataIn);
+
+    processorSocket(result);
+}
+
+std::string Cpu::processorSocket(std::string value)
+{
+    return sdbusplus::xyz::openbmc_project::Inventory::Item::server::Cpu::
+        processorSocket(value);
+}
+
+void Cpu::cpuType(const uint8_t value)
+{
+    std::map<uint8_t, const char *>::const_iterator it =
+        processorTypeTable.find(value);
+    if (it == processorTypeTable.end())
+    {
+        processorType("Unknown Processor Type");
+    }
+    else
+    {
+        processorType(it->second);
+    }
+}
+
+std::string Cpu::processorType(std::string value)
+{
+    return sdbusplus::xyz::openbmc_project::Inventory::Item::server::Cpu::
+        processorType(value);
+}
+
+void Cpu::cpuFamily(const uint8_t value)
+{
+    std::map<uint8_t, const char *>::const_iterator it =
+        familyTable.find(value);
+    if (it == familyTable.end())
+    {
+        processorFamily("Unknown Processor Family");
+    }
+    else
+    {
+        processorFamily(it->second);
+    }
+}
+
+std::string Cpu::processorFamily(std::string value)
+{
+    return sdbusplus::xyz::openbmc_project::Inventory::Item::server::Cpu::
+        processorFamily(value);
+}
+
+void Cpu::cpuManufacturer(const uint8_t positionNum, const uint8_t structLen,
+                          uint8_t *dataIn)
+{
+    std::string result = positionToString(positionNum, structLen, dataIn);
+
+    manufacturer(result);
+}
+
+std::string Cpu::manufacturer(std::string value)
+{
+    return sdbusplus::xyz::openbmc_project::Inventory::Decorator::server::
+        Asset::manufacturer(value);
+}
+
+uint32_t Cpu::processorId(uint32_t value)
+{
+    return sdbusplus::xyz::openbmc_project::Inventory::Item::server::Cpu::
+        processorId(value);
+}
+
+void Cpu::cpuVersion(const uint8_t positionNum, const uint8_t structLen,
+                     uint8_t *dataIn)
+{
+    std::string result;
+
+    result = positionToString(positionNum, structLen, dataIn);
+
+    processorVersion(result);
+}
+
+std::string Cpu::processorVersion(std::string value)
+{
+    return sdbusplus::xyz::openbmc_project::Inventory::Item::server::Cpu::
+        processorVersion(value);
+}
+
+uint16_t Cpu::processorMaxSpeed(uint16_t value)
+{
+    return sdbusplus::xyz::openbmc_project::Inventory::Item::server::Cpu::
+        processorMaxSpeed(value);
+}
+
+void Cpu::cpuCharacteristics(uint16_t value)
+{
+    std::string result = "";
+    for (uint8_t index = 0; index < (8 * sizeof(value)); index++)
+    {
+        if (value & 0x01)
+        {
+            result += characteristicsTable[index];
+        }
+        value >>= 1;
+    }
+
+    processorCharacteristics(result);
+}
+
+std::string Cpu::processorCharacteristics(std::string value)
+{
+    return sdbusplus::xyz::openbmc_project::Inventory::Item::server::Cpu::
+        processorCharacteristics(value);
+}
+
+uint16_t Cpu::processorCoreCount(uint16_t value)
+{
+    return sdbusplus::xyz::openbmc_project::Inventory::Item::server::Cpu::
+        processorCoreCount(value);
+}
+
+uint16_t Cpu::processorThreadCount(uint16_t value)
+{
+    return sdbusplus::xyz::openbmc_project::Inventory::Item::server::Cpu::
+        processorThreadCount(value);
+}
+
+static constexpr const uint8_t populateMask = 1 << 6;
+static constexpr const uint8_t statusMask = 0x07;
+void Cpu::cpuStatus(uint8_t value)
+{
+    if (!(value & populateMask))
+    {
+        present(false);
+        functional(false);
+        return;
+    }
+    present(true);
+    if ((value & statusMask) == 1)
+    {
+        functional(true);
+    }
+    else
+    {
+        functional(false);
+    }
+}
+
+bool Cpu::present(bool value)
+{
+    return sdbusplus::xyz::openbmc_project::Inventory::server::Item::present(
+        value);
+}
+
+bool Cpu::functional(bool value)
+{
+    return sdbusplus::xyz::openbmc_project::State::Decorator::server::
+        OperationalStatus::functional(value);
+}
+
+static constexpr uint8_t maxOldVersionCount = 0xff;
+void Cpu::processorInfoUpdate(void)
+{
+    uint8_t *dataIn = storage;
+
+    dataIn = getSMBIOSTypePtr(dataIn, processorsType);
+    if (dataIn == nullptr)
+    {
+        return;
+    }
+
+    for (uint8_t index = 0; index < cpuNum; index++)
+    {
+        dataIn = smbiosNextPtr(dataIn);
+        if (dataIn == nullptr)
+        {
+            return;
+        }
+        dataIn = getSMBIOSTypePtr(dataIn, processorsType);
+        if (dataIn == nullptr)
+        {
+            return;
+        }
+    }
+
+    auto cpuInfo = reinterpret_cast<struct ProcessorInfo *>(dataIn);
+
+    cpuSocket(cpuInfo->socketDesignation, cpuInfo->length,
+              dataIn);               // offset 4h
+    cpuType(cpuInfo->processorType); // offset 5h
+    cpuFamily(cpuInfo->family);      // offset 6h
+    cpuManufacturer(cpuInfo->manufacturer, cpuInfo->length,
+                    dataIn);                               // offset 7h
+    processorId(cpuInfo->id);                              // offset 8h
+    cpuVersion(cpuInfo->version, cpuInfo->length, dataIn); // offset 10h
+    processorMaxSpeed(cpuInfo->maxSpeed);                  // offset 14h
+    if (cpuInfo->coreCount < maxOldVersionCount)           // offset 23h or 2Ah
+    {
+        processorCoreCount(cpuInfo->coreCount);
+    }
+    else
+    {
+        processorCoreCount(cpuInfo->coreCount2);
+    }
+
+    if (cpuInfo->threadCount < maxOldVersionCount) // offset 25h or 2Eh)
+    {
+        processorThreadCount(cpuInfo->threadCount);
+    }
+    else
+    {
+        processorThreadCount(cpuInfo->threadCount2);
+    }
+
+    cpuCharacteristics(cpuInfo->characteristics); // offset 26h
+
+    cpuStatus(cpuInfo->status);
+}
+
+} // namespace smbios
+} // namespace phosphor
diff --git a/src/mdrv2.cpp b/src/mdrv2.cpp
index d940285..8452741 100644
--- a/src/mdrv2.cpp
+++ b/src/mdrv2.cpp
@@ -361,6 +361,60 @@
         directoryEntries(value);
 }
 
+void MDR_V2::systemInfoUpdate()
+{
+    cpus.clear();
+
+    int num = getTotalCpuSlot();
+    if (num == -1)
+    {
+        phosphor::logging::log<phosphor::logging::level::ERR>(
+            "get cpu total slot failed");
+        return;
+    }
+
+    for (int index = 0; index < num; index++)
+    {
+        std::string path = cpuPath + std::to_string(index);
+        cpus.emplace_back(std::make_unique<phosphor::smbios::Cpu>(
+            bus, path, index, smbiosDir.dir[smbiosDirIndex].dataStorage));
+    }
+}
+
+int MDR_V2::getTotalCpuSlot()
+{
+    uint8_t* dataIn = smbiosDir.dir[smbiosDirIndex].dataStorage;
+    int num = 0;
+
+    if (dataIn == nullptr)
+    {
+        phosphor::logging::log<phosphor::logging::level::ERR>(
+            "get cpu total slot failed - no storage data");
+        return -1;
+    }
+
+    while (1)
+    {
+        dataIn = getSMBIOSTypePtr(dataIn, processorsType);
+        if (dataIn == nullptr)
+        {
+            break;
+        }
+        num++;
+        dataIn = smbiosNextPtr(dataIn);
+        if (dataIn == nullptr)
+        {
+            break;
+        }
+        if (num >= limitEntryLen)
+        {
+            break;
+        }
+    }
+
+    return num;
+}
+
 bool MDR_V2::agentSynchronizeData()
 {
     struct MDRSMBIOSHeader mdr2SMBIOS;
@@ -374,6 +428,7 @@
     }
     else
     {
+        systemInfoUpdate();
         smbiosDir.dir[smbiosDirIndex].common.dataVersion = mdr2SMBIOS.dirVer;
         smbiosDir.dir[smbiosDirIndex].common.timestamp = mdr2SMBIOS.timestamp;
         smbiosDir.dir[smbiosDirIndex].common.size = mdr2SMBIOS.dataSize;