Implement HostEpoch set time logic

1. When setting host epoch, follow below logic:

        Mode  | Owner | Set Host Time
        ----- | ----- | -------------
        NTP   | BMC   | Not allowed
        NTP   | HOST  | Not allowed
        NTP   | SPLIT | OK, and just save offset
        NTP   | BOTH  | Not allowed
        MANUAL| BMC   | Not allowed
        MANUAL| HOST  | OK, and set time to BMC
        MANUAL| SPLIT | OK, and just save offset
        MANUAL| BOTH  | OK, and set time to BMC

2. If owner is SPLIT and BMC time is changed, update the offset accordinly;
3. Use timerfd to get notified on BMC time change, and update host time
diff accordingly;
4. Add unit test cases.

Change-Id: I2d60a821f7da9b689c579ae7ab672cc37967322c
Signed-off-by: Lei YU <mine260309@gmail.com>
diff --git a/host_epoch.cpp b/host_epoch.cpp
index 3b4c00e..129d224 100644
--- a/host_epoch.cpp
+++ b/host_epoch.cpp
@@ -9,43 +9,107 @@
 {
 using namespace sdbusplus::xyz::openbmc_project::Time;
 using namespace phosphor::logging;
+using namespace std::chrono;
 
 HostEpoch::HostEpoch(sdbusplus::bus::bus& bus,
                      const char* objPath)
     : EpochBase(bus, objPath),
       offset(utils::readData<decltype(offset)::rep>(offsetFile))
 {
-    // Empty
+    // Initialize the diffToSteadyClock
+    auto steadyTime = duration_cast<microseconds>(
+        steady_clock::now().time_since_epoch());
+    diffToSteadyClock = getTime() + offset - steadyTime;
 }
 
 uint64_t HostEpoch::elapsed() const
 {
-    // It does not needs to check owner when getting time
-    return (getTime() + offset).count();
+    auto ret = getTime();
+    if (timeOwner == Owner::SPLIT)
+    {
+        ret += offset;
+    }
+    return ret.count();
 }
 
 uint64_t HostEpoch::elapsed(uint64_t value)
 {
-    if (timeOwner == Owner::BMC)
+    /*
+        Mode  | Owner | Set Host Time
+        ----- | ----- | -------------
+        NTP   | BMC   | Not allowed
+        NTP   | HOST  | Not allowed
+        NTP   | SPLIT | OK, and just save offset
+        NTP   | BOTH  | Not allowed
+        MANUAL| BMC   | Not allowed
+        MANUAL| HOST  | OK, and set time to BMC
+        MANUAL| SPLIT | OK, and just save offset
+        MANUAL| BOTH  | OK, and set time to BMC
+    */
+    if (timeOwner == Owner::BMC ||
+        (timeMode == Mode::NTP
+         && (timeOwner == Owner::HOST || timeOwner == Owner::BOTH)))
     {
-        log<level::ERR>("Setting HostTime in BMC owner is not allowed");
+        log<level::ERR>("Setting HostTime is not allowed");
         // TODO: throw NotAllowed exception
         return 0;
     }
 
-    // TODO: implement the logic of setting host time
-    // based on timeOwner and timeMode
+    auto time = microseconds(value);
+    if (timeOwner == Owner::SPLIT)
+    {
+        // Calculate the offset between host and bmc time
+        offset = time - getTime();
+        saveOffset();
 
-    auto time = std::chrono::microseconds(value);
-    offset = time - getTime();
-
-    // Store the offset to file
-    utils::writeData(offsetFile, offset.count());
+        // Calculate the diff between host and steady time
+        auto steadyTime = duration_cast<microseconds>(
+            steady_clock::now().time_since_epoch());
+        diffToSteadyClock = time - steadyTime;
+    }
+    else
+    {
+        // Set time to BMC
+        setTime(time);
+    }
 
     server::EpochTime::elapsed(value);
     return value;
 }
 
+void HostEpoch::onOwnerChanged(Owner owner)
+{
+    // If timeOwner is changed to SPLIT, the offset shall be preserved
+    // Otherwise it shall be cleared;
+    timeOwner = owner;
+    if (timeOwner != Owner::SPLIT)
+    {
+        offset = microseconds(0);
+        saveOffset();
+    }
+}
+
+void HostEpoch::saveOffset()
+{
+    // Store the offset to file
+    utils::writeData(offsetFile, offset.count());
+}
+
+void HostEpoch::onBmcTimeChanged(const microseconds& bmcTime)
+{
+    // If owner is split and BMC time is changed,
+    // the offset shall be adjusted
+    if (timeOwner == Owner::SPLIT)
+    {
+        auto steadyTime = duration_cast<microseconds>(
+            steady_clock::now().time_since_epoch());
+        auto hostTime = steadyTime + diffToSteadyClock;
+        offset = hostTime - bmcTime;
+
+        saveOffset();
+    }
+}
+
 } // namespace time
 } // namespace phosphor