blob: 7e44864e60277e500f3baf1c5b026c45aa6d8744 [file] [log] [blame]
Steve Foreman4f0d1de2021-09-20 14:06:32 -07001// Copyright 2022 Google LLC
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15#include "google_accel_oob.hpp"
16
17#include "commands.hpp"
18
Patrick Williams444b5ea2023-05-19 13:56:42 -050019#include <sdbusplus/bus.hpp>
Michael Shen8d618532023-10-25 09:14:07 +000020#include <stdplus/print.hpp>
Patrick Williams444b5ea2023-05-19 13:56:42 -050021
Steve Foreman4f0d1de2021-09-20 14:06:32 -070022#include <cstdint>
23#include <cstring>
Steve Foreman4f0d1de2021-09-20 14:06:32 -070024#include <span>
25#include <string>
26#include <vector>
27
28namespace google
29{
30namespace ipmi
31{
32
33#ifndef MAX_IPMI_BUFFER
34#define MAX_IPMI_BUFFER 64
35#endif
36
37// token + address(8) + num_bytes + data(8) + len + NULL
38constexpr size_t MAX_NAME_SIZE = MAX_IPMI_BUFFER - 1 - 8 - 1 - 8 - 1 - 1;
39
40Resp accelOobDeviceCount(std::span<const uint8_t> data,
41 HandlerInterface* handler)
42{
43 struct Request
44 {
45 } __attribute__((packed));
46
47 struct Reply
48 {
49 uint32_t count;
50 } __attribute__((packed));
51
52 if (data.size_bytes() < sizeof(Request))
53 {
Michael Shen8d618532023-10-25 09:14:07 +000054 stdplus::print(stderr, "AccelOob DeviceCount command too small: {}\n",
55 data.size_bytes());
Steve Foreman4f0d1de2021-09-20 14:06:32 -070056 return ::ipmi::responseReqDataLenInvalid();
57 }
58
59 if (data.size_bytes() + sizeof(Reply) > MAX_IPMI_BUFFER)
60 {
Michael Shen8d618532023-10-25 09:14:07 +000061 stdplus::print(
62 stderr,
63 "AccelOob DeviceCount command too large for reply buffer: "
64 "command={}B, payload={}B, max={}B\n",
65 data.size_bytes(), sizeof(Reply), MAX_IPMI_BUFFER);
Steve Foreman4f0d1de2021-09-20 14:06:32 -070066 return ::ipmi::responseReqDataLenExceeded();
67 }
68
69 uint32_t count = handler->accelOobDeviceCount();
70
71 std::vector<uint8_t> replyBuf(sizeof(Reply));
72 auto* reply = reinterpret_cast<Reply*>(replyBuf.data());
73 reply->count = count;
74
75 return ::ipmi::responseSuccess(SysOEMCommands::SysAccelOobDeviceCount,
76 replyBuf);
77}
78
79Resp accelOobDeviceName(std::span<const uint8_t> data,
80 HandlerInterface* handler)
81{
82 struct Request
83 {
84 uint32_t index;
85 } __attribute__((packed));
86
87 struct Reply
88 {
89 uint8_t nameLength;
90 char name[MAX_NAME_SIZE];
91 } __attribute__((packed));
92
93 if (data.size_bytes() < sizeof(Request))
94 {
Michael Shen8d618532023-10-25 09:14:07 +000095 stdplus::print(stderr, "AccelOob DeviceName command too small: {}\n",
96 data.size_bytes());
Steve Foreman4f0d1de2021-09-20 14:06:32 -070097 return ::ipmi::responseReqDataLenInvalid();
98 }
99
100 if (data.size_bytes() + sizeof(Reply) > MAX_IPMI_BUFFER)
101 {
Michael Shen8d618532023-10-25 09:14:07 +0000102 stdplus::print(
103 stderr,
104 "AccelOob DeviceName command too large for reply buffer: "
105 "command={}B, payload={}B, max={}B\n",
106 data.size_bytes(), sizeof(Reply), MAX_IPMI_BUFFER);
Steve Foreman4f0d1de2021-09-20 14:06:32 -0700107 return ::ipmi::responseReqDataLenExceeded();
108 }
109
110 auto* req = reinterpret_cast<const Request*>(data.data());
111 std::string name = handler->accelOobDeviceName(req->index);
112
113 if (name.size() > MAX_NAME_SIZE)
114 {
Michael Shen8d618532023-10-25 09:14:07 +0000115 stdplus::print(stderr,
116 "AccelOob: name was too long. "
117 "'{}' len must be <= {}\n",
118 name, MAX_NAME_SIZE);
Steve Foreman4f0d1de2021-09-20 14:06:32 -0700119 return ::ipmi::responseReqDataTruncated();
120 }
121
122 std::vector<uint8_t> replyBuf(data.size_bytes() + sizeof(Reply));
123 std::copy(data.begin(), data.end(), replyBuf.data());
124 auto* reply = reinterpret_cast<Reply*>(replyBuf.data() + data.size_bytes());
125 reply->nameLength = name.length();
126 memcpy(reply->name, name.c_str(), reply->nameLength + 1);
127
128 return ::ipmi::responseSuccess(SysOEMCommands::SysAccelOobDeviceName,
129 replyBuf);
130}
131
132namespace
133{
134
135struct NameHeader
136{
137 uint8_t nameLength;
138 char name[MAX_NAME_SIZE];
139} __attribute__((packed));
140
141// Reads the variable-length name from reqBuf and outputs the name and a pointer
142// to the payload (next byte after name).
143//
144// Returns: =0: success.
145// >0: if dataLen is too small, returns the minimum buffers size.
146//
147// Params:
148// [in] reqBuf - the request buffer
149// [in] dataLen - the length of reqBuf, in bytes
150// [in] payloadSize - the size of the expected payload
151// [out] name - the name string
152// [out] payload - pointer into reqBuf just after name
153size_t ReadNameHeader(const uint8_t* reqBuf, size_t dataLen, size_t payloadSize,
154 std::string* name, const uint8_t** payload)
155{
156 constexpr size_t kNameHeaderSize = sizeof(NameHeader) - MAX_NAME_SIZE;
157
158 auto* req_header = reinterpret_cast<const NameHeader*>(reqBuf);
159
160 size_t minDataLen = kNameHeaderSize + payloadSize + req_header->nameLength;
161 if (dataLen < minDataLen)
162 {
163 return minDataLen;
164 }
165
166 if (name)
167 {
168 *name = std::string(req_header->name, req_header->nameLength);
169 }
170 if (payload)
171 {
172 *payload = reqBuf + kNameHeaderSize + req_header->nameLength;
173 }
174 return 0;
175}
176
177} // namespace
178
179Resp accelOobRead(std::span<const uint8_t> data, HandlerInterface* handler)
180{
181 struct Request
182 {
183 // Variable length header, handled by ReadNameHeader
184 // uint8_t nameLength; // <= MAX_NAME_SIZE
185 // char name[nameLength];
186
187 // Additional arguments
188 uint8_t token;
189 uint64_t address;
190 uint8_t num_bytes;
191 } __attribute__((packed));
192
193 struct Reply
194 {
195 uint64_t data;
196 } __attribute__((packed));
197
Michael Shen8d618532023-10-25 09:14:07 +0000198 stdplus::print(stderr,
199 "AccelOob Read command sizes: "
200 "command={}B, payload={}B, max={}B\n",
201 data.size_bytes(), sizeof(Reply), MAX_IPMI_BUFFER);
Steve Foreman4f0d1de2021-09-20 14:06:32 -0700202
203 std::string name;
204 const uint8_t* payload;
205
206 size_t min_size = ReadNameHeader(data.data(), data.size_bytes(),
207 sizeof(Request), &name, &payload);
208 if (min_size != 0)
209 {
Michael Shen8d618532023-10-25 09:14:07 +0000210 stdplus::print(stderr, "AccelOob Read command too small: {}B < {}B\n",
211 data.size_bytes(), min_size);
Steve Foreman4f0d1de2021-09-20 14:06:32 -0700212 return ::ipmi::responseReqDataLenInvalid();
213 }
214
215 if (data.size_bytes() + sizeof(Reply) > MAX_IPMI_BUFFER)
216 {
Michael Shen8d618532023-10-25 09:14:07 +0000217 stdplus::print(stderr,
218 "AccelOob Read command too large for reply buffer: "
219 "command={}B, payload={}B, max={}B\n",
220 data.size_bytes(), sizeof(Reply), MAX_IPMI_BUFFER);
Steve Foreman4f0d1de2021-09-20 14:06:32 -0700221 return ::ipmi::responseReqDataLenExceeded();
222 }
223
224 auto req = reinterpret_cast<const Request*>(payload);
225 uint64_t r = handler->accelOobRead(name, req->address, req->num_bytes);
226
227 std::vector<uint8_t> replyBuf(data.size_bytes() + sizeof(Reply));
228 std::copy(data.begin(), data.end(), replyBuf.data());
229 auto* reply = reinterpret_cast<Reply*>(replyBuf.data() + data.size_bytes());
230 reply->data = r;
231
232 return ::ipmi::responseSuccess(SysOEMCommands::SysAccelOobRead, replyBuf);
233}
234
235Resp accelOobWrite(std::span<const uint8_t> data, HandlerInterface* handler)
236{
237 struct Request
238 {
239 // Variable length header, handled by ReadNameHeader
240 // uint8_t nameLength; // <= MAX_NAME_SIZE
241 // char name[nameLength];
242
243 // Additional arguments
244 uint8_t token;
245 uint64_t address;
246 uint8_t num_bytes;
247 uint64_t data;
248 } __attribute__((packed));
249
250 struct Reply
251 {
252 // Empty
253 } __attribute__((packed));
254
255 std::string name{};
256 const uint8_t* payload;
257
258 size_t min_size = ReadNameHeader(data.data(), data.size_bytes(),
259 sizeof(Request), &name, &payload);
260 if (min_size != 0)
261 {
Michael Shen8d618532023-10-25 09:14:07 +0000262 stdplus::print(stderr, "AccelOob Write command too small: {}B < {}B\n",
263 data.size_bytes(), min_size);
Steve Foreman4f0d1de2021-09-20 14:06:32 -0700264 return ::ipmi::responseReqDataLenInvalid();
265 }
266
267 if (data.size_bytes() + sizeof(Reply) > MAX_IPMI_BUFFER)
268 {
Michael Shen8d618532023-10-25 09:14:07 +0000269 stdplus::print(stderr,
270 "AccelOob Write command too large for reply buffer: "
271 "command={}B, payload={}B, max={}B\n",
272 data.size_bytes(), sizeof(Reply), MAX_IPMI_BUFFER);
Steve Foreman4f0d1de2021-09-20 14:06:32 -0700273 return ::ipmi::responseReqDataLenExceeded();
274 }
275
276 auto req = reinterpret_cast<const Request*>(payload);
277 handler->accelOobWrite(name, req->address, req->num_bytes, req->data);
278
279 std::vector<uint8_t> replyBuf(data.size_bytes() + sizeof(Reply));
280 std::copy(data.begin(), data.end(), replyBuf.data());
281
282 return ::ipmi::responseSuccess(SysOEMCommands::SysAccelOobWrite, replyBuf);
283}
284
285} // namespace ipmi
286} // namespace google