Add IPMI interface for CustomAccel service
Change-Id: I28a8976e382b457233ac521e9ab71f75abe029d1
Signed-off-by: Steve Foreman <foremans@google.com>
diff --git a/google_accel_oob.cpp b/google_accel_oob.cpp
new file mode 100644
index 0000000..e0fa615
--- /dev/null
+++ b/google_accel_oob.cpp
@@ -0,0 +1,282 @@
+// 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.
+// 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 "google_accel_oob.hpp"
+
+#include "commands.hpp"
+
+#include <cstdint>
+#include <cstring>
+#include <sdbusplus/bus.hpp>
+#include <span>
+#include <string>
+#include <vector>
+
+namespace google
+{
+namespace ipmi
+{
+
+#ifndef MAX_IPMI_BUFFER
+#define MAX_IPMI_BUFFER 64
+#endif
+
+// token + address(8) + num_bytes + data(8) + len + NULL
+constexpr size_t MAX_NAME_SIZE = MAX_IPMI_BUFFER - 1 - 8 - 1 - 8 - 1 - 1;
+
+Resp accelOobDeviceCount(std::span<const uint8_t> data,
+ HandlerInterface* handler)
+{
+ struct Request
+ {
+ } __attribute__((packed));
+
+ struct Reply
+ {
+ uint32_t count;
+ } __attribute__((packed));
+
+ if (data.size_bytes() < sizeof(Request))
+ {
+ std::fprintf(stderr, "AccelOob DeviceCount command too small: %zu\n",
+ data.size_bytes());
+ return ::ipmi::responseReqDataLenInvalid();
+ }
+
+ if (data.size_bytes() + sizeof(Reply) > MAX_IPMI_BUFFER)
+ {
+ std::fprintf(stderr,
+ "AccelOob DeviceCount command too large for reply buffer: "
+ "command=%zuB, payload=%zuB, max=%dB\n",
+ data.size_bytes(), sizeof(Reply), MAX_IPMI_BUFFER);
+ return ::ipmi::responseReqDataLenExceeded();
+ }
+
+ uint32_t count = handler->accelOobDeviceCount();
+
+ std::vector<uint8_t> replyBuf(sizeof(Reply));
+ auto* reply = reinterpret_cast<Reply*>(replyBuf.data());
+ reply->count = count;
+
+ return ::ipmi::responseSuccess(SysOEMCommands::SysAccelOobDeviceCount,
+ replyBuf);
+}
+
+Resp accelOobDeviceName(std::span<const uint8_t> data,
+ HandlerInterface* handler)
+{
+ struct Request
+ {
+ uint32_t index;
+ } __attribute__((packed));
+
+ struct Reply
+ {
+ uint8_t nameLength;
+ char name[MAX_NAME_SIZE];
+ } __attribute__((packed));
+
+ if (data.size_bytes() < sizeof(Request))
+ {
+ std::fprintf(stderr, "AccelOob DeviceName command too small: %zu\n",
+ data.size_bytes());
+ return ::ipmi::responseReqDataLenInvalid();
+ }
+
+ if (data.size_bytes() + sizeof(Reply) > MAX_IPMI_BUFFER)
+ {
+ std::fprintf(stderr,
+ "AccelOob DeviceName command too large for reply buffer: "
+ "command=%zuB, payload=%zuB, max=%dB\n",
+ data.size_bytes(), sizeof(Reply), MAX_IPMI_BUFFER);
+ return ::ipmi::responseReqDataLenExceeded();
+ }
+
+ auto* req = reinterpret_cast<const Request*>(data.data());
+ std::string name = handler->accelOobDeviceName(req->index);
+
+ if (name.size() > MAX_NAME_SIZE)
+ {
+ std::fprintf(stderr,
+ "AccelOob: name was too long. "
+ "'%s' len must be <= %zu\n",
+ name.c_str(), MAX_NAME_SIZE);
+ return ::ipmi::responseReqDataTruncated();
+ }
+
+ std::vector<uint8_t> replyBuf(data.size_bytes() + sizeof(Reply));
+ std::copy(data.begin(), data.end(), replyBuf.data());
+ auto* reply = reinterpret_cast<Reply*>(replyBuf.data() + data.size_bytes());
+ reply->nameLength = name.length();
+ memcpy(reply->name, name.c_str(), reply->nameLength + 1);
+
+ return ::ipmi::responseSuccess(SysOEMCommands::SysAccelOobDeviceName,
+ replyBuf);
+}
+
+namespace
+{
+
+struct NameHeader
+{
+ uint8_t nameLength;
+ char name[MAX_NAME_SIZE];
+} __attribute__((packed));
+
+// Reads the variable-length name from reqBuf and outputs the name and a pointer
+// to the payload (next byte after name).
+//
+// Returns: =0: success.
+// >0: if dataLen is too small, returns the minimum buffers size.
+//
+// Params:
+// [in] reqBuf - the request buffer
+// [in] dataLen - the length of reqBuf, in bytes
+// [in] payloadSize - the size of the expected payload
+// [out] name - the name string
+// [out] payload - pointer into reqBuf just after name
+size_t ReadNameHeader(const uint8_t* reqBuf, size_t dataLen, size_t payloadSize,
+ std::string* name, const uint8_t** payload)
+{
+ constexpr size_t kNameHeaderSize = sizeof(NameHeader) - MAX_NAME_SIZE;
+
+ auto* req_header = reinterpret_cast<const NameHeader*>(reqBuf);
+
+ size_t minDataLen = kNameHeaderSize + payloadSize + req_header->nameLength;
+ if (dataLen < minDataLen)
+ {
+ return minDataLen;
+ }
+
+ if (name)
+ {
+ *name = std::string(req_header->name, req_header->nameLength);
+ }
+ if (payload)
+ {
+ *payload = reqBuf + kNameHeaderSize + req_header->nameLength;
+ }
+ return 0;
+}
+
+} // namespace
+
+Resp accelOobRead(std::span<const uint8_t> data, HandlerInterface* handler)
+{
+ struct Request
+ {
+ // Variable length header, handled by ReadNameHeader
+ // uint8_t nameLength; // <= MAX_NAME_SIZE
+ // char name[nameLength];
+
+ // Additional arguments
+ uint8_t token;
+ uint64_t address;
+ uint8_t num_bytes;
+ } __attribute__((packed));
+
+ struct Reply
+ {
+ uint64_t data;
+ } __attribute__((packed));
+
+ std::fprintf(stderr,
+ "AccelOob Read command sizes: "
+ "command=%zuB, payload=%zuB, max=%dB\n",
+ data.size_bytes(), sizeof(Reply), MAX_IPMI_BUFFER);
+
+ std::string name;
+ const uint8_t* payload;
+
+ size_t min_size = ReadNameHeader(data.data(), data.size_bytes(),
+ sizeof(Request), &name, &payload);
+ if (min_size != 0)
+ {
+ std::fprintf(stderr, "AccelOob Read command too small: %zuB < %zuB\n",
+ data.size_bytes(), min_size);
+ return ::ipmi::responseReqDataLenInvalid();
+ }
+
+ if (data.size_bytes() + sizeof(Reply) > MAX_IPMI_BUFFER)
+ {
+ std::fprintf(stderr,
+ "AccelOob Read command too large for reply buffer: "
+ "command=%zuB, payload=%zuB, max=%dB\n",
+ data.size_bytes(), sizeof(Reply), MAX_IPMI_BUFFER);
+ return ::ipmi::responseReqDataLenExceeded();
+ }
+
+ auto req = reinterpret_cast<const Request*>(payload);
+ uint64_t r = handler->accelOobRead(name, req->address, req->num_bytes);
+
+ std::vector<uint8_t> replyBuf(data.size_bytes() + sizeof(Reply));
+ std::copy(data.begin(), data.end(), replyBuf.data());
+ auto* reply = reinterpret_cast<Reply*>(replyBuf.data() + data.size_bytes());
+ reply->data = r;
+
+ return ::ipmi::responseSuccess(SysOEMCommands::SysAccelOobRead, replyBuf);
+}
+
+Resp accelOobWrite(std::span<const uint8_t> data, HandlerInterface* handler)
+{
+ struct Request
+ {
+ // Variable length header, handled by ReadNameHeader
+ // uint8_t nameLength; // <= MAX_NAME_SIZE
+ // char name[nameLength];
+
+ // Additional arguments
+ uint8_t token;
+ uint64_t address;
+ uint8_t num_bytes;
+ uint64_t data;
+ } __attribute__((packed));
+
+ struct Reply
+ {
+ // Empty
+ } __attribute__((packed));
+
+ std::string name{};
+ const uint8_t* payload;
+
+ size_t min_size = ReadNameHeader(data.data(), data.size_bytes(),
+ sizeof(Request), &name, &payload);
+ if (min_size != 0)
+ {
+ std::fprintf(stderr, "AccelOob Write command too small: %zuB < %zuB\n",
+ data.size_bytes(), min_size);
+ return ::ipmi::responseReqDataLenInvalid();
+ }
+
+ if (data.size_bytes() + sizeof(Reply) > MAX_IPMI_BUFFER)
+ {
+ std::fprintf(stderr,
+ "AccelOob Write command too large for reply buffer: "
+ "command=%zuB, payload=%zuB, max=%dB\n",
+ data.size_bytes(), sizeof(Reply), MAX_IPMI_BUFFER);
+ return ::ipmi::responseReqDataLenExceeded();
+ }
+
+ auto req = reinterpret_cast<const Request*>(payload);
+ handler->accelOobWrite(name, req->address, req->num_bytes, req->data);
+
+ std::vector<uint8_t> replyBuf(data.size_bytes() + sizeof(Reply));
+ std::copy(data.begin(), data.end(), replyBuf.data());
+
+ return ::ipmi::responseSuccess(SysOEMCommands::SysAccelOobWrite, replyBuf);
+}
+
+} // namespace ipmi
+} // namespace google