summaryrefslogtreecommitdiff
path: root/meta-openbmc-mods/meta-common/recipes-phosphor/datetime/pch-time-sync
diff options
context:
space:
mode:
Diffstat (limited to 'meta-openbmc-mods/meta-common/recipes-phosphor/datetime/pch-time-sync')
-rw-r--r--meta-openbmc-mods/meta-common/recipes-phosphor/datetime/pch-time-sync/.clang-format98
-rw-r--r--meta-openbmc-mods/meta-common/recipes-phosphor/datetime/pch-time-sync/CMakeLists.txt40
-rw-r--r--meta-openbmc-mods/meta-common/recipes-phosphor/datetime/pch-time-sync/cmake-format.json12
-rw-r--r--meta-openbmc-mods/meta-common/recipes-phosphor/datetime/pch-time-sync/pch-time-sync.cpp431
-rw-r--r--meta-openbmc-mods/meta-common/recipes-phosphor/datetime/pch-time-sync/pch-time-sync.service15
5 files changed, 596 insertions, 0 deletions
diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/datetime/pch-time-sync/.clang-format b/meta-openbmc-mods/meta-common/recipes-phosphor/datetime/pch-time-sync/.clang-format
new file mode 100644
index 000000000..dd2770837
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-phosphor/datetime/pch-time-sync/.clang-format
@@ -0,0 +1,98 @@
+---
+Language: Cpp
+# BasedOnStyle: LLVM
+AccessModifierOffset: -2
+AlignAfterOpenBracket: Align
+AlignConsecutiveAssignments: false
+AlignConsecutiveDeclarations: false
+AlignEscapedNewlinesLeft: false
+AlignOperands: true
+AlignTrailingComments: true
+AllowAllParametersOfDeclarationOnNextLine: true
+AllowShortBlocksOnASingleLine: false
+AllowShortCaseLabelsOnASingleLine: false
+AllowShortFunctionsOnASingleLine: None
+AllowShortIfStatementsOnASingleLine: false
+AllowShortLoopsOnASingleLine: false
+AlwaysBreakAfterDefinitionReturnType: None
+AlwaysBreakAfterReturnType: None
+AlwaysBreakBeforeMultilineStrings: false
+AlwaysBreakTemplateDeclarations: false
+BinPackArguments: true
+BinPackParameters: true
+BraceWrapping:
+ AfterClass: true
+ AfterControlStatement: true
+ AfterEnum: true
+ AfterFunction: true
+ AfterNamespace: true
+ AfterObjCDeclaration: true
+ AfterStruct: true
+ AfterUnion: true
+ BeforeCatch: true
+ BeforeElse: true
+ IndentBraces: false
+BreakBeforeBinaryOperators: None
+BreakBeforeBraces: Custom
+BreakBeforeTernaryOperators: true
+BreakConstructorInitializers: AfterColon
+ColumnLimit: 80
+CommentPragmas: '^ IWYU pragma:'
+ConstructorInitializerAllOnOneLineOrOnePerLine: false
+ConstructorInitializerIndentWidth: 4
+ContinuationIndentWidth: 4
+Cpp11BracedListStyle: true
+DerivePointerAlignment: true
+PointerAlignment: Left
+DisableFormat: false
+ExperimentalAutoDetectBinPacking: false
+FixNamespaceComments: true
+ForEachMacros: [ foreach, Q_FOREACH, BOOST_FOREACH ]
+IncludeBlocks: Regroup
+IncludeCategories:
+ - Regex: '^[<"](gtest|gmock)'
+ Priority: 5
+ - Regex: '^"config.h"'
+ Priority: -1
+ - Regex: '^".*\.hpp"'
+ Priority: 1
+ - Regex: '^<.*\.h>'
+ Priority: 2
+ - Regex: '^<.*'
+ Priority: 3
+ - Regex: '.*'
+ Priority: 4
+IndentCaseLabels: true
+IndentWidth: 4
+IndentWrappedFunctionNames: true
+KeepEmptyLinesAtTheStartOfBlocks: true
+MacroBlockBegin: ''
+MacroBlockEnd: ''
+MaxEmptyLinesToKeep: 1
+NamespaceIndentation: None
+ObjCBlockIndentWidth: 2
+ObjCSpaceAfterProperty: false
+ObjCSpaceBeforeProtocolList: true
+PenaltyBreakBeforeFirstCallParameter: 19
+PenaltyBreakComment: 300
+PenaltyBreakFirstLessLess: 120
+PenaltyBreakString: 1000
+PenaltyExcessCharacter: 1000000
+PenaltyReturnTypeOnItsOwnLine: 60
+PointerAlignment: Right
+ReflowComments: true
+SortIncludes: true
+SpaceAfterCStyleCast: false
+SpaceBeforeAssignmentOperators: true
+SpaceBeforeParens: ControlStatements
+SpaceInEmptyParentheses: false
+SpacesBeforeTrailingComments: 1
+SpacesInAngles: false
+SpacesInContainerLiterals: true
+SpacesInCStyleCastParentheses: false
+SpacesInParentheses: false
+SpacesInSquareBrackets: false
+Standard: Cpp11
+TabWidth: 4
+UseTab: Never
+...
diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/datetime/pch-time-sync/CMakeLists.txt b/meta-openbmc-mods/meta-common/recipes-phosphor/datetime/pch-time-sync/CMakeLists.txt
new file mode 100644
index 000000000..a4cf8155f
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-phosphor/datetime/pch-time-sync/CMakeLists.txt
@@ -0,0 +1,40 @@
+cmake_minimum_required (VERSION 3.5 FATAL_ERROR)
+project (pch-time-sync CXX)
+set (CMAKE_CXX_STANDARD 17)
+set (CMAKE_CXX_STANDARD_REQUIRED ON)
+set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-rtti")
+set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fno-rtti")
+
+include_directories (${CMAKE_CURRENT_SOURCE_DIR})
+
+# boost support
+find_package (Boost REQUIRED)
+# pkg_check_modules(Boost boost REQUIRED)
+include_directories (${Boost_INCLUDE_DIRS})
+add_definitions (-DBOOST_ERROR_CODE_HEADER_ONLY)
+add_definitions (-DBOOST_SYSTEM_NO_DEPRECATED)
+add_definitions (-DBOOST_ALL_NO_LIB)
+add_definitions (-DBOOST_NO_RTTI)
+add_definitions (-DBOOST_NO_TYPEID)
+add_definitions (-DBOOST_ASIO_DISABLE_THREADS)
+
+# import sdbusplus
+find_package (PkgConfig REQUIRED)
+pkg_check_modules (SDBUSPLUSPLUS sdbusplus REQUIRED)
+include_directories (${SDBUSPLUSPLUS_INCLUDE_DIRS})
+link_directories (${SDBUSPLUSPLUS_LIBRARY_DIRS})
+
+# import phosphor-logging
+find_package (PkgConfig REQUIRED)
+pkg_check_modules (LOGGING phosphor-logging REQUIRED)
+include_directories (${LOGGING_INCLUDE_DIRS})
+link_directories (${LOGGING_LIBRARY_DIRS})
+
+add_executable (pch-time-sync pch-time-sync.cpp)
+
+target_link_libraries (${PROJECT_NAME} ${Boost_LIBRARIES})
+target_link_libraries (${PROJECT_NAME} ${SDBUSPLUSPLUS_LIBRARIES}
+ phosphor_logging)
+target_link_libraries (${PROJECT_NAME} i2c)
+
+install (TARGETS pch-time-sync DESTINATION bin)
diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/datetime/pch-time-sync/cmake-format.json b/meta-openbmc-mods/meta-common/recipes-phosphor/datetime/pch-time-sync/cmake-format.json
new file mode 100644
index 000000000..583c255a3
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-phosphor/datetime/pch-time-sync/cmake-format.json
@@ -0,0 +1,12 @@
+{
+ "enum_char": ".",
+ "line_ending": "unix",
+ "bullet_char": "*",
+ "max_subargs_per_line": 99,
+ "command_case": "lower",
+ "tab_size": 4,
+ "line_width": 80,
+ "separate_fn_name_with_space": true,
+ "dangle_parens": true,
+ "separate_ctrl_name_with_space": true
+} \ No newline at end of file
diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/datetime/pch-time-sync/pch-time-sync.cpp b/meta-openbmc-mods/meta-common/recipes-phosphor/datetime/pch-time-sync/pch-time-sync.cpp
new file mode 100644
index 000000000..454bdc1db
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-phosphor/datetime/pch-time-sync/pch-time-sync.cpp
@@ -0,0 +1,431 @@
+/* Copyright 2019 Intel
+ *
+ * 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 <systemd/sd-journal.h>
+#include <time.h>
+
+#include <boost/asio/io_service.hpp>
+#include <boost/asio/steady_timer.hpp>
+#include <boost/date_time/posix_time/posix_time.hpp>
+#include <chrono>
+#include <filesystem>
+#include <iostream>
+#include <phosphor-logging/log.hpp>
+#include <sdbusplus/asio/object_server.hpp>
+extern "C" {
+#include <i2c/smbus.h>
+#include <linux/i2c-dev.h>
+}
+
+static constexpr uint32_t syncIntervalNormalMS = 60000;
+static constexpr uint32_t syncIntervalFastMS = (syncIntervalNormalMS / 2);
+static constexpr uint32_t syncIntervalBootMS = 5000;
+
+static uint32_t syncIntervalMS = syncIntervalNormalMS;
+
+// will update bmc time if the time difference beyond this value
+static constexpr uint8_t timeDiffAllowedSecond = 1;
+static uint8_t pchDevI2cBusNo = 0;
+static uint8_t pchDevI2cSlaveAddr = 0;
+static bool getPCHI2cAddrFlag = false;
+static constexpr const char* clockFile = "/var/lib/systemd/timesync/clock";
+static inline uint8_t bcd2Decimal(uint8_t hex)
+{
+ uint8_t dec = ((hex & 0xF0) >> 4) * 10 + (hex & 0x0F);
+ return dec;
+}
+
+class I2CFile
+{
+ private:
+ int fd = -1;
+
+ public:
+ I2CFile(const int& i2cBus, const int& slaveAddr, const int& flags)
+ {
+ std::string i2cDev = "/dev/i2c-" + std::to_string(i2cBus);
+
+ fd = open(i2cDev.c_str(), flags);
+ if (fd < 0)
+ {
+ throw std::runtime_error("Unable to open i2c device.");
+ }
+
+ if (ioctl(fd, I2C_SLAVE_FORCE, slaveAddr) < 0)
+ {
+ close(fd);
+ fd = -1;
+ throw std::runtime_error("Unable to set i2c slave address.");
+ }
+ }
+
+ uint8_t i2cReadByteData(const uint8_t& offset)
+ {
+ int ret = i2c_smbus_read_byte_data(fd, offset);
+
+ if (ret < 0)
+ {
+ throw std::runtime_error("i2c read failed");
+ }
+ return static_cast<uint8_t>(ret);
+ }
+
+ ~I2CFile()
+ {
+ if (!(fd < 0))
+ {
+ close(fd);
+ }
+ }
+};
+
+static void getPCHI2cAddr(std::shared_ptr<sdbusplus::asio::connection>& conn,
+ const std::string& service, const std::string& object,
+ const std::string& interface)
+{
+ conn->async_method_call(
+ [](boost::system::error_code ec,
+ const std::vector<
+ std::pair<std::string, std::variant<std::string, uint64_t>>>&
+ propertiesList) {
+ if (ec)
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "DBUS response error: cannot get I2c address of PCH timer",
+ phosphor::logging::entry("ECVALUE=%x", ec.value()),
+ phosphor::logging::entry("ECMESSAGE=%s",
+ ec.message().c_str()));
+ return;
+ }
+ const uint64_t* i2cBusNoValue = nullptr;
+ const uint64_t* i2cSlaveAddrValue = nullptr;
+ for (const auto& property : propertiesList)
+ {
+
+ if (property.first == "PchSmbusSlaveI2cBus")
+ {
+ i2cBusNoValue = std::get_if<uint64_t>(&property.second);
+ }
+ if (property.first == "PchSmbusSlaveI2cAddress")
+ {
+ i2cSlaveAddrValue = std::get_if<uint64_t>(&property.second);
+ }
+ }
+ if ((i2cBusNoValue != nullptr) && (i2cSlaveAddrValue != nullptr))
+ {
+ pchDevI2cBusNo = static_cast<uint8_t>(*i2cBusNoValue);
+ pchDevI2cSlaveAddr = static_cast<uint8_t>(*i2cSlaveAddrValue);
+ getPCHI2cAddrFlag = true;
+ }
+ },
+ service, object, "org.freedesktop.DBus.Properties", "GetAll",
+ interface);
+}
+
+static void
+ getPCHTimerConfiguration(std::shared_ptr<sdbusplus::asio::connection>& conn)
+{
+ conn->async_method_call(
+ [&conn](
+ boost::system::error_code ec,
+ const std::vector<std::pair<
+ std::string,
+ std::vector<std::pair<std::string, std::vector<std::string>>>>>&
+ subtree) {
+ if (ec)
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "DBUS response error:cannot get PCH configuration",
+ phosphor::logging::entry("ECVALUE=%x", ec.value()),
+ phosphor::logging::entry("ECMESSAGE=%s",
+ ec.message().c_str()));
+ return;
+ }
+ if (subtree.empty())
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "subtree empty");
+ return;
+ }
+ getPCHI2cAddr(conn, subtree[0].second[0].first, subtree[0].first,
+ "xyz.openbmc_project.Configuration.PchSmbusSlave");
+ return;
+ },
+ "xyz.openbmc_project.ObjectMapper",
+ "/xyz/openbmc_project/object_mapper",
+ "xyz.openbmc_project.ObjectMapper", "GetSubTree",
+ "/xyz/openbmc_project/", 0,
+ std::array<const char*, 1>{
+ "xyz.openbmc_project.Configuration.PchSmbusSlave"});
+
+ return;
+}
+
+class PCHSync
+{
+ private:
+ bool getPCHDate(uint8_t& year, uint8_t& month, uint8_t& day, uint8_t& hour,
+ uint8_t& minute, uint8_t& second)
+ {
+ try
+ {
+ constexpr uint8_t pchDevRegRTCYear = 0x0f;
+ constexpr uint8_t pchDevRegRTCMonth = 0x0e;
+ constexpr uint8_t pchDevRegRTCDay = 0x0d;
+ constexpr uint8_t pchDevRegRTCHour = 0x0b;
+ constexpr uint8_t pchDevRegRTCMinute = 0x0a;
+ constexpr uint8_t pchDevRegRTCSecond = 0x09;
+ I2CFile pchDev(pchDevI2cBusNo, pchDevI2cSlaveAddr,
+ O_RDWR | O_CLOEXEC);
+ year = pchDev.i2cReadByteData(pchDevRegRTCYear);
+ year = bcd2Decimal(year);
+ if (year > 99)
+ {
+ return false;
+ }
+
+ month = pchDev.i2cReadByteData(pchDevRegRTCMonth);
+ month = bcd2Decimal(month);
+ if ((month < 1) || (month > 12))
+ {
+ return false;
+ }
+
+ day = pchDev.i2cReadByteData(pchDevRegRTCDay);
+ day = bcd2Decimal(day);
+ if ((day < 1) || (day > 31))
+ {
+ return false;
+ }
+
+ hour = pchDev.i2cReadByteData(pchDevRegRTCHour);
+ hour = bcd2Decimal(hour);
+ if (hour >= 24)
+ {
+ return false;
+ }
+
+ minute = pchDev.i2cReadByteData(pchDevRegRTCMinute);
+ minute = bcd2Decimal(minute);
+ if (minute >= 60)
+ {
+ return false;
+ }
+
+ second = pchDev.i2cReadByteData(pchDevRegRTCSecond);
+ second = bcd2Decimal(second);
+ if (second >= 60)
+ {
+ return false;
+ }
+ }
+ catch (const std::exception& e)
+ {
+ return false;
+ }
+
+ return true;
+ }
+
+ bool getSystemTime(time_t& timeSeconds)
+ {
+ struct timespec sTime = {0};
+ int ret = 0;
+
+ ret = clock_gettime(CLOCK_REALTIME, &sTime);
+
+ if (ret != 0)
+ {
+ return false;
+ }
+ timeSeconds = sTime.tv_sec;
+ return true;
+ }
+
+ bool updateClockFileTimestamp()
+ {
+ if (!std::filesystem::exists(clockFile))
+ {
+ phosphor::logging::log<phosphor::logging::level::WARNING>(
+ "The systemd timestamp synchronization file doesn't exist: ",
+ phosphor::logging::entry("PATHNAME=%s", clockFile));
+ return false;
+ }
+ int rc = utimensat(AT_FDCWD, clockFile, nullptr, 0);
+ if (rc)
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "utimensat systemd timestamp synchronization file fail: ",
+ phosphor::logging::entry("PATHNAME=%s", clockFile),
+ phosphor::logging::entry("ERRCODE=%x", errno));
+ return false;
+ }
+ return true;
+ }
+ bool setSystemTime(uint32_t timeSeconds)
+ {
+ struct timespec sTime = {0};
+ int ret = 0;
+
+ sTime.tv_sec = timeSeconds;
+ sTime.tv_nsec = 0;
+
+ ret = clock_settime(CLOCK_REALTIME, &sTime);
+
+ return (ret == 0);
+ }
+
+ bool updateBMCTime()
+ {
+ int ret = 0;
+ time_t BMCTimeSeconds = 0;
+ time_t PCHTimeSeconds = 0;
+ struct tm tm = {0};
+
+ // get PCH and system time
+
+ if (!getPCHDate(year, month, day, hour, minute, second))
+ {
+ return false;
+ };
+ if (!getSystemTime(BMCTimeSeconds))
+ {
+ return false;
+ }
+ // fix error when year is set to 2000-2009.
+ std::string dateString =
+ std::to_string(2000 + year) + "-" + std::to_string(month) + "-" +
+ std::to_string(day) + " " + std::to_string(hour) + ":" +
+ std::to_string(minute) + ":" + std::to_string(second);
+
+ strptime(dateString.c_str(), "%Y-%m-%d %H:%M:%S", &tm);
+
+ PCHTimeSeconds = mktime(&tm);
+ if (PCHTimeSeconds == -1)
+ {
+ return false;
+ }
+
+ if (std::abs(PCHTimeSeconds - BMCTimeSeconds) > timeDiffAllowedSecond)
+ {
+ if (!setSystemTime(PCHTimeSeconds))
+ {
+ return false;
+ }
+ char newDateString[32] = {0};
+ strftime(newDateString, sizeof(newDateString), "%F %T",
+ gmtime(&PCHTimeSeconds));
+
+ struct tm* timeinfo;
+ char oldDateString[32] = {0};
+ timeinfo = gmtime(&BMCTimeSeconds);
+ strftime(oldDateString, sizeof(oldDateString), "%F %T", timeinfo);
+
+ // Log event about BMC time update via PCH
+ sd_journal_send(
+ "MESSAGE=BMC time updated via PCH: New time=%s, Old time=%s",
+ newDateString, oldDateString, "PRIORITY=%i", LOG_INFO,
+ "REDFISH_MESSAGE_ID=%s", "OpenBMC.0.1.BMCTimeUpdatedViaHost",
+ "REDFISH_MESSAGE_ARGS=%s,%s", newDateString, oldDateString,
+ NULL);
+ }
+
+ // During the boot time, systemd-timesyncd.service checks
+ // "/var/lib/systemd/timesync/clock" and updates the system time with
+ // the timestamp of the file
+ if (!updateClockFileTimestamp())
+ {
+ return false;
+ }
+
+ return true;
+ }
+
+ void startSyncTimer(std::shared_ptr<sdbusplus::asio::connection>& conn)
+ {
+ // retry 10 times (10 * 5s = 50s ) to get the pch timer
+ // configuration.
+ static uint8_t retrytimes = 10;
+ if (!getPCHI2cAddrFlag)
+ {
+ if (retrytimes == 0)
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Get pch timer configuration fail");
+ return;
+ }
+ syncIntervalMS = syncIntervalBootMS;
+ getPCHTimerConfiguration(conn);
+ retrytimes--;
+ }
+ else
+ {
+ if (updateBMCTime())
+ {
+ syncIntervalMS = syncIntervalNormalMS;
+ }
+ else
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Update BMC time Fail");
+ syncIntervalMS = syncIntervalFastMS;
+ }
+ }
+
+ syncTimer->expires_after(std::chrono::milliseconds(syncIntervalMS));
+ syncTimer->async_wait(
+ [this, &conn](const boost::system::error_code& ec) {
+ if (ec)
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Timer cancelled",
+ phosphor::logging::entry("ECVALUE=%x", ec.value()),
+ phosphor::logging::entry("ECMESSAGE=%s",
+ ec.message().c_str()));
+ return;
+ }
+ startSyncTimer(conn);
+ });
+ }
+
+ std::unique_ptr<boost::asio::steady_timer> syncTimer;
+ uint8_t year, month, day, hour, minute, second;
+
+ public:
+ PCHSync(boost::asio::io_service& io,
+ std::shared_ptr<sdbusplus::asio::connection>& conn)
+ {
+ syncTimer = std::make_unique<boost::asio::steady_timer>(io);
+ startSyncTimer(conn);
+ }
+
+ ~PCHSync() = default;
+};
+
+int main(int argc, char** argv)
+{
+ boost::asio::io_service io;
+ std::shared_ptr<sdbusplus::asio::connection> conn =
+ std::make_shared<sdbusplus::asio::connection>(io);
+ sdbusplus::asio::object_server server =
+ sdbusplus::asio::object_server(conn);
+ PCHSync pchSyncer(io, conn);
+
+ phosphor::logging::log<phosphor::logging::level::INFO>(
+ "Starting PCH time sync service");
+
+ io.run();
+ return 0;
+}
diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/datetime/pch-time-sync/pch-time-sync.service b/meta-openbmc-mods/meta-common/recipes-phosphor/datetime/pch-time-sync/pch-time-sync.service
new file mode 100644
index 000000000..bf4e2a30e
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-phosphor/datetime/pch-time-sync/pch-time-sync.service
@@ -0,0 +1,15 @@
+[Unit]
+Description=PCH BMC time sync service
+Conflicts=systemd-timesyncd.service
+Requires=xyz.openbmc_project.EntityManager.service
+After=xyz.openbmc_project.EntityManager.service
+
+[Service]
+Restart=always
+RestartSec=10
+ExecStart=/usr/bin/pch-time-sync
+StartLimitInterval=0
+Type=simple
+
+[Install]
+WantedBy=multi-user.target