Add D-Bus Raw PECI Daemon

This adds a new D-Bus daemon to handle raw PECI requests over
D-Bus.

The D-Bus interface provides a Send method that takes a
vector of byte vectors where each byte vector contains the raw
data for a single PECI command.  This allows sending an array
of raw PECI commands to execute in bulk to avoid issues with
network latency when sending multiple commands.

The method returns a vector of raw PECI responses corresponding
to the vector of raw commands.

Tested:
Confirmed that a single and multiple PECI commands correctly
execute and return when SendRawPeci is called.

Change-Id: I6b2d0f81366ea77057e70d69c72be90ba4c4aeb9
Signed-off-by: Jason M. Bills <jason.m.bills@linux.intel.com>
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 441b320..398f396 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -7,6 +7,8 @@
 target_include_directories(peci PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
 set_target_properties(peci PROPERTIES VERSION "1.0" SOVERSION "1")
 
+option(DBUS_RAW_PECI "Add the raw PECI D-Bus daemon to the build." OFF)
+
 set(
   CMAKE_C_FLAGS
   "${CMAKE_C_FLAGS} \
@@ -38,3 +40,28 @@
         RUNTIME DESTINATION bin
         LIBRARY DESTINATION lib
         ARCHIVE DESTINATION lib/static)
+
+if(${DBUS_RAW_PECI})
+  add_executable(raw-peci dbus_raw_peci.cpp)
+  add_dependencies(raw-peci peci)
+
+  find_package(Boost 1.73 REQUIRED)
+  include_directories(${BOOST_SRC_DIR})
+
+  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)
+
+  target_link_libraries(raw-peci peci -lsystemd sdbusplus)
+
+  install(TARGETS raw-peci
+          RUNTIME DESTINATION bin
+          LIBRARY DESTINATION lib
+          ARCHIVE DESTINATION lib/static)
+
+  set(SERVICE_FILES ${PROJECT_SOURCE_DIR}/service_files/com.intel.peci.service)
+
+  install(FILES ${SERVICE_FILES} DESTINATION /lib/systemd/system/)
+endif()
\ No newline at end of file
diff --git a/README.md b/README.md
index bc825ec..a6a1374 100644
--- a/README.md
+++ b/README.md
@@ -10,3 +10,9 @@
 This repo also includes a peci_cmds command-line utility with functions that
 map to the libpeci APIs. It can be used to test PECI functionality across
 the library, driver, and hardware.
+
+## dbus_raw_peci
+This repo also includes dbus_raw_peci which provides a raw-peci daemon that
+exposes a raw PECI interface that is accessible over D-Bus.  It can be used
+when an application needs to send a raw PECI command without loading the full
+PECI library.
diff --git a/dbus_raw_peci.cpp b/dbus_raw_peci.cpp
new file mode 100644
index 0000000..49e1c5d
--- /dev/null
+++ b/dbus_raw_peci.cpp
@@ -0,0 +1,82 @@
+/*
+// Copyright (c) 2021 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 <peci.h>
+
+#include <boost/asio/io_service.hpp>
+#include <iostream>
+#include <sdbusplus/asio/object_server.hpp>
+
+int main()
+{
+    boost::asio::io_service io;
+    std::shared_ptr<sdbusplus::asio::connection> conn;
+    std::shared_ptr<sdbusplus::asio::object_server> server;
+
+    // setup connection to dbus
+    conn = std::make_shared<sdbusplus::asio::connection>(io);
+
+    conn->request_name("com.intel.peci");
+    server = std::make_shared<sdbusplus::asio::object_server>(conn);
+
+    // Send Raw PECI Interface
+    std::shared_ptr<sdbusplus::asio::dbus_interface> ifaceRawPeci =
+        server->add_interface("/com/intel/peci", "com.intel.Protocol.PECI.Raw");
+
+    // Send a Raw PECI command
+    ifaceRawPeci->register_method(
+        "Send", [](const std::string& peciDev,
+                   const std::vector<std::vector<uint8_t>>& rawCmds) {
+            peci_SetDevName(const_cast<char*>(peciDev.c_str()));
+            // D-Bus will time out after too long, so set a deadline for when to
+            // abort the PECI commands (at 25s, it mostly times out, at 24s it
+            // doesn't, so use 23s to be safe)
+            constexpr int peciTimeout = 23;
+            std::chrono::steady_clock::time_point peciDeadline =
+                std::chrono::steady_clock::now() +
+                std::chrono::duration<int>(peciTimeout);
+            std::vector<std::vector<uint8_t>> rawResp;
+            rawResp.resize(rawCmds.size());
+            for (size_t i = 0; i < rawCmds.size(); i++)
+            {
+                const std::vector<uint8_t>& rawCmd = rawCmds[i];
+                // If the commands are taking too long, return early to avoid a
+                // D-Bus timeout
+                if (std::chrono::steady_clock::now() > peciDeadline)
+                {
+                    std::cerr << peciTimeout
+                              << " second deadline reached.  Aborting PECI "
+                                 "commands to avoid a timeout\n";
+                    break;
+                }
+
+                if (rawCmd.size() < 3)
+                {
+                    peci_SetDevName(NULL);
+                    throw std::invalid_argument("Command Length too short");
+                }
+                rawResp[i].resize(rawCmd[2]);
+                peci_raw(rawCmd[0], rawCmd[2], &rawCmd[3], rawCmd[1],
+                         rawResp[i].data(), rawResp[i].size());
+            }
+            peci_SetDevName(NULL);
+            return rawResp;
+        });
+    ifaceRawPeci->initialize();
+
+    io.run();
+
+    return 0;
+}
\ No newline at end of file
diff --git a/service_files/com.intel.peci.service b/service_files/com.intel.peci.service
new file mode 100644
index 0000000..88c7025
--- /dev/null
+++ b/service_files/com.intel.peci.service
@@ -0,0 +1,11 @@
+[Unit]
+Description=Intel CPU Raw PECI interface
+
+[Service]
+Restart=always
+ExecStart=/usr/bin/raw-peci
+Type=dbus
+BusName=com.intel.peci
+
+[Install]
+WantedBy=multi-user.target