Add IPMI interface for CustomAccel service
Change-Id: I28a8976e382b457233ac521e9ab71f75abe029d1
Signed-off-by: Steve Foreman <foremans@google.com>
diff --git a/handler.cpp b/handler.cpp
index c716af6..8ec7863 100644
--- a/handler.cpp
+++ b/handler.cpp
@@ -1,4 +1,4 @@
-// Copyright 2021 Google LLC
+// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -38,6 +38,7 @@
#include <string>
#include <string_view>
#include <tuple>
+#include <variant>
#include <xyz/openbmc_project/Common/error.hpp>
#ifndef NCSI_IF_NAME
@@ -392,5 +393,211 @@
return _pcie_i2c_map[entry];
}
+namespace
+{
+
+static constexpr std::string_view ACCEL_OOB_ROOT = "/com/google/customAccel/";
+static constexpr char ACCEL_OOB_SERVICE[] = "com.google.custom_accel";
+static constexpr char ACCEL_OOB_INTERFACE[] = "com.google.custom_accel.BAR";
+
+// C type for "a{oa{sa{sv}}}" from DBus.ObjectManager::GetManagedObjects()
+using AnyType = std::variant<std::string, uint8_t, uint32_t, uint64_t>;
+using AnyTypeList = std::vector<std::pair<std::string, AnyType>>;
+using NamedArrayOfAnyTypeLists =
+ std::vector<std::pair<std::string, AnyTypeList>>;
+using ArrayOfObjectPathsAndTieredAnyTypeLists = std::vector<
+ std::pair<sdbusplus::message::object_path, NamedArrayOfAnyTypeLists>>;
+
+} // namespace
+
+sdbusplus::bus::bus Handler::accelOobGetDbus() const
+{
+ return sdbusplus::bus::new_default();
+}
+
+uint32_t Handler::accelOobDeviceCount() const
+{
+ ArrayOfObjectPathsAndTieredAnyTypeLists data;
+
+ try
+ {
+ auto bus = accelOobGetDbus();
+ auto method = bus.new_method_call(ACCEL_OOB_SERVICE, "/",
+ "org.freedesktop.DBus.ObjectManager",
+ "GetManagedObjects");
+ bus.call(method).read(data);
+ }
+ catch (const sdbusplus::exception::SdBusError& ex)
+ {
+ log<level::ERR>(
+ "Failed to call GetManagedObjects on com.google.custom_accel",
+ entry("WHAT=%s", ex.what()));
+ throw IpmiException(IPMI_CC_UNSPECIFIED_ERROR);
+ }
+
+ return data.size();
+}
+
+std::string Handler::accelOobDeviceName(size_t index) const
+{
+ ArrayOfObjectPathsAndTieredAnyTypeLists data;
+
+ try
+ {
+ auto bus = accelOobGetDbus();
+ auto method = bus.new_method_call(ACCEL_OOB_SERVICE, "/",
+ "org.freedesktop.DBus.ObjectManager",
+ "GetManagedObjects");
+ bus.call(method).read(data);
+ }
+ catch (const sdbusplus::exception::SdBusError& ex)
+ {
+ log<level::ERR>(
+ "Failed to call GetManagedObjects on com.google.custom_accel",
+ entry("WHAT=%s", ex.what()));
+ throw IpmiException(IPMI_CC_UNSPECIFIED_ERROR);
+ }
+
+ if (index >= data.size())
+ {
+ log<level::WARNING>(
+ "Requested index is larger than the number of entries.",
+ entry("INDEX=%zu", index), entry("NUM_NAMES=%zu", data.size()));
+ throw IpmiException(IPMI_CC_PARM_OUT_OF_RANGE);
+ }
+
+ std::string_view name(data[index].first.str);
+ if (!name.starts_with(ACCEL_OOB_ROOT))
+ {
+ throw IpmiException(IPMI_CC_INVALID);
+ }
+ name.remove_prefix(ACCEL_OOB_ROOT.length());
+ return std::string(name);
+}
+
+uint64_t Handler::accelOobRead(std::string_view name, uint64_t address,
+ uint8_t num_bytes) const
+{
+ static constexpr char ACCEL_OOB_METHOD[] = "Read";
+
+ std::string object_name(ACCEL_OOB_ROOT);
+ object_name.append(name);
+
+ auto bus = accelOobGetDbus();
+ auto method = bus.new_method_call(ACCEL_OOB_SERVICE, object_name.c_str(),
+ ACCEL_OOB_INTERFACE, ACCEL_OOB_METHOD);
+ method.append(address, static_cast<uint64_t>(num_bytes));
+
+ std::vector<uint8_t> bytes;
+
+ try
+ {
+ bus.call(method).read(bytes);
+ }
+ catch (const sdbusplus::exception::SdBusError& ex)
+ {
+ log<level::ERR>("Failed to call Read on com.google.custom_accel",
+ entry("WHAT=%s", ex.what()),
+ entry("DBUS_SERVICE=%s", ACCEL_OOB_SERVICE),
+ entry("DBUS_OBJECT=%s", object_name.c_str()),
+ entry("DBUS_INTERFACE=%s", ACCEL_OOB_INTERFACE),
+ entry("DBUS_METHOD=%s", ACCEL_OOB_METHOD),
+ entry("DBUS_ARG_ADDRESS=%016llx", address),
+ entry("DBUS_ARG_NUM_BYTES=%zu", (size_t)num_bytes));
+ throw IpmiException(IPMI_CC_UNSPECIFIED_ERROR);
+ }
+
+ if (bytes.size() < num_bytes)
+ {
+ log<level::ERR>(
+ "Call to Read on com.google.custom_accel didn't return the expected"
+ " number of bytes.",
+ entry("DBUS_SERVICE=%s", ACCEL_OOB_SERVICE),
+ entry("DBUS_OBJECT=%s", object_name.c_str()),
+ entry("DBUS_INTERFACE=%s", ACCEL_OOB_INTERFACE),
+ entry("DBUS_METHOD=%s", ACCEL_OOB_METHOD),
+ entry("DBUS_ARG_ADDRESS=%016llx", address),
+ entry("DBUS_ARG_NUM_BYTES=%zu", (size_t)num_bytes),
+ entry("DBUS_RETURN_SIZE=%zu", bytes.size()));
+ throw IpmiException(IPMI_CC_UNSPECIFIED_ERROR);
+ }
+
+ if (bytes.size() > sizeof(uint64_t))
+ {
+ log<level::ERR>(
+ "Call to Read on com.google.custom_accel returned more than 8B.",
+ entry("DBUS_SERVICE=%s", ACCEL_OOB_SERVICE),
+ entry("DBUS_OBJECT=%s", object_name.c_str()),
+ entry("DBUS_INTERFACE=%s", ACCEL_OOB_INTERFACE),
+ entry("DBUS_METHOD=%s", ACCEL_OOB_METHOD),
+ entry("DBUS_ARG_ADDRESS=%016llx", address),
+ entry("DBUS_ARG_NUM_BYTES=%zu", (size_t)num_bytes),
+ entry("DBUS_RETURN_SIZE=%zu", bytes.size()));
+ throw IpmiException(IPMI_CC_REQ_DATA_TRUNCATED);
+ }
+
+ uint64_t data = 0;
+ for (size_t i = 0; i < num_bytes; ++i)
+ {
+ data = (data << 8) | bytes[i];
+ }
+
+ return data;
+}
+
+void Handler::accelOobWrite(std::string_view name, uint64_t address,
+ uint8_t num_bytes, uint64_t data) const
+{
+ static constexpr std::string_view ACCEL_OOB_METHOD = "Write";
+
+ std::string object_name(ACCEL_OOB_ROOT);
+ object_name.append(name);
+
+ if (num_bytes > sizeof(data))
+ {
+ log<level::ERR>(
+ "Call to Write on com.google.custom_accel requested more than 8B.",
+ entry("DBUS_SERVICE=%s", ACCEL_OOB_SERVICE),
+ entry("DBUS_OBJECT=%s", object_name.c_str()),
+ entry("DBUS_INTERFACE=%s", ACCEL_OOB_INTERFACE),
+ entry("DBUS_METHOD=%s", ACCEL_OOB_METHOD.data()),
+ entry("DBUS_ARG_ADDRESS=%016llx", address),
+ entry("DBUS_ARG_NUM_BYTES=%zu", (size_t)num_bytes),
+ entry("DBUS_ARG_DATA=%016llx", data));
+ throw IpmiException(IPMI_CC_PARM_OUT_OF_RANGE);
+ }
+
+ std::vector<uint8_t> bytes;
+ bytes.reserve(num_bytes);
+ for (size_t i = 0; i < num_bytes; ++i)
+ {
+ bytes.emplace_back(data & 0xff);
+ data >>= 8;
+ }
+
+ try
+ {
+ auto bus = accelOobGetDbus();
+ auto method =
+ bus.new_method_call(ACCEL_OOB_SERVICE, object_name.c_str(),
+ ACCEL_OOB_INTERFACE, ACCEL_OOB_METHOD.data());
+ method.append(address, bytes);
+ bus.call_noreply(method);
+ }
+ catch (const sdbusplus::exception::SdBusError& ex)
+ {
+ log<level::ERR>("Failed to call Write on com.google.custom_accel",
+ entry("WHAT=%s", ex.what()),
+ entry("DBUS_SERVICE=%s", ACCEL_OOB_SERVICE),
+ entry("DBUS_OBJECT=%s", object_name.c_str()),
+ entry("DBUS_INTERFACE=%s", ACCEL_OOB_INTERFACE),
+ entry("DBUS_METHOD=%s", ACCEL_OOB_METHOD.data()),
+ entry("DBUS_ARG_ADDRESS=%016llx", address),
+ entry("DBUS_ARG_NUM_BYTES=%zu", (size_t)num_bytes),
+ entry("DBUS_ARG_DATA=%016llx", data));
+ throw IpmiException(IPMI_CC_UNSPECIFIED_ERROR);
+ }
+}
+
} // namespace ipmi
} // namespace google