Add pfr postcode feature

Add PfrPostcode object to read platformState mailbox register.
Add dbus object to PFR manager service so current platform state
value can be read by interested application like Redfish.
PfrPostcode object is added only when system is pfr provisioned.

Tested:
Manually verified dbus object properties as below -

busctl introspect xyz.openbmc_project.PFR.Manager /xyz/openbmc_project/pfr
NAME                                    TYPE      SIGNATURE  RESULT/VALUE  FLAGS
xyz.openbmc_project.State.Boot.Platform interface -          -                  -
.Data                                   property  y          14                 emits-change writable
.PlatformState                          property  s          "T0 boot complete" emits-change

Signed-off-by: Zhikui Ren <zhikui.ren@intel.com>
Change-Id: I3f3581af23c2656523ee975b2aab9ba15078c934
diff --git a/libpfr/inc/pfr.hpp b/libpfr/inc/pfr.hpp
index 6dbf455..fb42b23 100644
--- a/libpfr/inc/pfr.hpp
+++ b/libpfr/inc/pfr.hpp
@@ -51,6 +51,7 @@
 std::string getFirmwareVersion(const ImageType& imgType);
 int getProvisioningStatus(bool& ufmLocked, bool& ufmProvisioned,
                           bool& ufmSupport);
+int getPlatformState(uint8_t& state);
 int readCpldReg(const ActionType& action, uint8_t& value);
 std::string readCPLDVersion();
 int setBMCBootCheckpoint(const uint8_t checkPoint);
diff --git a/libpfr/src/pfr.cpp b/libpfr/src/pfr.cpp
index 77320fa..0402641 100644
--- a/libpfr/src/pfr.cpp
+++ b/libpfr/src/pfr.cpp
@@ -485,6 +485,24 @@
     }
 }
 
+int getPlatformState(uint8_t& state)
+{
+    try
+    {
+        I2CFile cpldDev(i2cBusNumber, i2cSlaveAddress, O_RDWR | O_CLOEXEC);
+        state = cpldDev.i2cReadByteData(platformState);
+
+        return 0;
+    }
+    catch (const std::exception& e)
+    {
+        phosphor::logging::log<phosphor::logging::level::ERR>(
+            "Exception caught in getPlatformState.",
+            phosphor::logging::entry("MSG=%s", e.what()));
+        return -1;
+    }
+}
+
 int readCpldReg(const ActionType& action, uint8_t& value)
 {
     uint8_t cpldReg;
diff --git a/service/inc/pfr_mgr.hpp b/service/inc/pfr_mgr.hpp
index c0d6fd5..d773e4f 100644
--- a/service/inc/pfr_mgr.hpp
+++ b/service/inc/pfr_mgr.hpp
@@ -75,6 +75,11 @@
 
     void updateProvisioningStatus();
 
+    bool getPfrProvisioned() const
+    {
+        return ufmProvisioned;
+    }
+
   private:
     sdbusplus::asio::object_server& server;
     std::shared_ptr<sdbusplus::asio::dbus_interface> pfrCfgIface;
@@ -92,4 +97,57 @@
     majorErrorCodeMapRev2 = {
         {0x03, {"FirmwareResiliencyError", "Firmware update failed"}}};
 
+// postcode (platform state) map.
+static const boost::container::flat_map<uint8_t, std::string> postcodeMap = {
+    {0x00, "Postcode unavailable"},
+    {0x01, "CPLD Nios II processor waiting to start"},
+    {0x02, "CPLD Nios II processor started"},
+    {0x03, "Enter T-1"},
+    {0x04, "T-1 reserved 4"},
+    {0x05, "T-1 Reserved 5"},
+    {0x06, "BMC flash authentication"},
+    {0x07, "PCH/CPU flash authentication"},
+    {0x08, "Lockdown due to authentication failures"},
+    {0x09, "Enter T0"},
+    {0x0A, "T0 BMC booted"},
+    {0x0B, "T0 ME booted"},
+    {0x0C, "T0 Modular booted"},
+    {0x0C, "T0 BIOS booted"},
+    {0x0E, "T0 boot complete"},
+    {0x0F, "T0 Reserved 0xF"},
+    {0x10, "PCH/CPU firmware update"},
+    {0x11, "BMC firmware update"},
+    {0x12, "CPLD update (in CPLD Active Image)"},
+    {0x13, "CPLD update (in CPLD ROM)"},
+    {0x14, "PCH/CPU firmware volume update"},
+    {0x15, "CPLD Nios II processor waiting to start"},
+    {0x16, "Reserved 0x16"},
+    {0x40, "T-1 firmware recovery due to authentication failure"},
+    {0x41, "T-1 forced active firmware recovery"},
+    {0x42, "WDT timeout recovery"},
+    {0x43, "CPLD recovery (in CPLD ROM)"},
+    {0x44, "Lockdown due to PIT L1"},
+    {0x45, "PIT L2 firmware sealed"},
+    {0x46, "Lockdown due to PIT L2 PCH/CPU firmware hash mismatch"},
+    {0x47, "Lockdown due to PIT L2 BMC firmware hash mismatch"},
+    {0x48, "Reserved 0x48"}};
+
+class PfrPostcode
+{
+  public:
+    PfrPostcode(sdbusplus::asio::object_server& srv_,
+                std::shared_ptr<sdbusplus::asio::connection>& conn_);
+    ~PfrPostcode() = default;
+
+    std::shared_ptr<sdbusplus::asio::connection> conn;
+
+    void updatePostcode();
+
+  private:
+    sdbusplus::asio::object_server& server;
+    std::shared_ptr<sdbusplus::asio::dbus_interface> pfrPostcodeIface;
+    bool internalSet = false;
+    uint8_t postcode;
+};
+
 } // namespace pfr
diff --git a/service/src/mainapp.cpp b/service/src/mainapp.cpp
index 0a986f5..65bea78 100644
--- a/service/src/mainapp.cpp
+++ b/service/src/mainapp.cpp
@@ -39,6 +39,7 @@
 std::unique_ptr<boost::asio::steady_timer> pfrObjTimer = nullptr;
 std::vector<std::unique_ptr<PfrVersion>> pfrVersionObjects;
 std::unique_ptr<PfrConfig> pfrConfigObject;
+std::unique_ptr<PfrPostcode> pfrPostcodeObject;
 
 // List holds <ObjPath> <ImageType> <VersionPurpose>
 static std::vector<std::tuple<std::string, ImageType, std::string>>
@@ -689,6 +690,16 @@
             std::get<2>(entry)));
     }
 
+    if (pfr::pfrConfigObject)
+    {
+        pfr::pfrConfigObject->updateProvisioningStatus();
+        if (pfr::pfrConfigObject->getPfrProvisioned())
+        {
+            pfr::pfrPostcodeObject =
+                std::make_unique<pfr::PfrPostcode>(server, conn);
+        }
+    }
+
     conn->request_name("xyz.openbmc_project.PFR.Manager");
     phosphor::logging::log<phosphor::logging::level::INFO>(
         "Intel PFR service started successfully");
diff --git a/service/src/pfr_mgr.cpp b/service/src/pfr_mgr.cpp
index 2fc73fd..fc69fea 100644
--- a/service/src/pfr_mgr.cpp
+++ b/service/src/pfr_mgr.cpp
@@ -213,4 +213,83 @@
     return;
 }
 
+static constexpr const char* postcodeStrProp = "PlatformState";
+static constexpr const char* postcodeStrDefault = "Unknown";
+static constexpr const char* postcodeDataProp = "Data";
+static constexpr const char* postcodeIface =
+    "xyz.openbmc_project.State.Boot.Platform";
+
+PfrPostcode::PfrPostcode(sdbusplus::asio::object_server& srv_,
+                         std::shared_ptr<sdbusplus::asio::connection>& conn_) :
+    server(srv_),
+    conn(conn_)
+{
+    if (getPlatformState(postcode) < 0)
+    {
+        postcode = 0;
+    }
+
+    pfrPostcodeIface =
+        server.add_interface("/xyz/openbmc_project/pfr", postcodeIface);
+
+    if (pfrPostcodeIface != nullptr)
+    {
+        pfrPostcodeIface->register_property(
+            postcodeDataProp, postcode,
+            // Override set
+            [this](const uint8_t req, uint8_t& propertyValue) {
+                if (internalSet)
+                {
+                    if (req != propertyValue)
+                    {
+                        postcode = req;
+                        propertyValue = req;
+                        return 1;
+                    }
+                }
+                return 0;
+            },
+            [this](uint8_t& propertyValue) {
+                updatePostcode();
+                propertyValue = postcode;
+                return propertyValue;
+            });
+
+        pfrPostcodeIface->register_property(postcodeStrProp,
+                                            std::string(postcodeStrDefault));
+
+        pfrPostcodeIface->initialize();
+        auto it = postcodeMap.find(postcode);
+        if (it != postcodeMap.end())
+        {
+            pfrPostcodeIface->set_property(postcodeStrProp, it->second);
+        }
+    }
+}
+
+void PfrPostcode::updatePostcode()
+{
+    if (pfrPostcodeIface && pfrPostcodeIface->is_initialized())
+    {
+        if (getPlatformState(postcode) < 0)
+        {
+            postcode = 0;
+        }
+
+        internalSet = true;
+        pfrPostcodeIface->set_property(postcodeDataProp, postcode);
+        auto it = postcodeMap.find(postcode);
+        if (it == postcodeMap.end())
+        {
+            pfrPostcodeIface->set_property(postcodeStrProp, postcodeStrDefault);
+        }
+        else
+        {
+            pfrPostcodeIface->set_property(postcodeStrProp, it->second);
+        }
+        internalSet = false;
+    }
+    return;
+}
+
 } // namespace pfr