1 // 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 #include "errors.hpp"
19 #include "handler.hpp"
20
21 #include <sdbusplus/bus.hpp>
22 #include <stdplus/print.hpp>
23
24 #include <cstdint>
25 #include <cstring>
26 #include <span>
27 #include <string>
28 #include <vector>
29
30 namespace google
31 {
32 namespace ipmi
33 {
34
35 #ifndef MAX_IPMI_BUFFER
36 #define MAX_IPMI_BUFFER 64
37 #endif
38
39 // token + address(8) + num_bytes + data(8) + len + NULL
40 constexpr size_t MAX_NAME_SIZE = MAX_IPMI_BUFFER - 1 - 8 - 1 - 8 - 1 - 1;
41
accelOobDeviceCount(std::span<const uint8_t> data,HandlerInterface * handler)42 Resp accelOobDeviceCount(std::span<const uint8_t> data,
43 HandlerInterface* handler)
44 {
45 struct Request
46 {
47 } __attribute__((packed));
48
49 struct Reply
50 {
51 uint32_t count;
52 } __attribute__((packed));
53
54 if (data.size_bytes() < sizeof(Request))
55 {
56 stdplus::print(stderr, "AccelOob DeviceCount command too small: {}\n",
57 data.size_bytes());
58 return ::ipmi::responseReqDataLenInvalid();
59 }
60
61 if (data.size_bytes() + sizeof(Reply) > MAX_IPMI_BUFFER)
62 {
63 stdplus::print(
64 stderr,
65 "AccelOob DeviceCount command too large for reply buffer: "
66 "command={}B, payload={}B, max={}B\n",
67 data.size_bytes(), sizeof(Reply), MAX_IPMI_BUFFER);
68 return ::ipmi::responseReqDataLenExceeded();
69 }
70
71 uint32_t count = handler->accelOobDeviceCount();
72
73 std::vector<uint8_t> replyBuf(sizeof(Reply));
74 auto* reply = reinterpret_cast<Reply*>(replyBuf.data());
75 reply->count = count;
76
77 return ::ipmi::responseSuccess(SysOEMCommands::SysAccelOobDeviceCount,
78 replyBuf);
79 }
80
accelOobDeviceName(std::span<const uint8_t> data,HandlerInterface * handler)81 Resp accelOobDeviceName(std::span<const uint8_t> data,
82 HandlerInterface* handler)
83 {
84 struct Request
85 {
86 uint32_t index;
87 } __attribute__((packed));
88
89 struct Reply
90 {
91 uint8_t nameLength;
92 char name[MAX_NAME_SIZE];
93 } __attribute__((packed));
94
95 if (data.size_bytes() < sizeof(Request))
96 {
97 stdplus::print(stderr, "AccelOob DeviceName command too small: {}\n",
98 data.size_bytes());
99 return ::ipmi::responseReqDataLenInvalid();
100 }
101
102 if (data.size_bytes() + sizeof(Reply) > MAX_IPMI_BUFFER)
103 {
104 stdplus::print(
105 stderr,
106 "AccelOob DeviceName command too large for reply buffer: "
107 "command={}B, payload={}B, max={}B\n",
108 data.size_bytes(), sizeof(Reply), MAX_IPMI_BUFFER);
109 return ::ipmi::responseReqDataLenExceeded();
110 }
111
112 auto* req = reinterpret_cast<const Request*>(data.data());
113 std::string name = handler->accelOobDeviceName(req->index);
114
115 if (name.size() > MAX_NAME_SIZE)
116 {
117 stdplus::print(stderr,
118 "AccelOob: name was too long. "
119 "'{}' len must be <= {}\n",
120 name, MAX_NAME_SIZE);
121 return ::ipmi::responseReqDataTruncated();
122 }
123
124 std::vector<uint8_t> replyBuf(data.size_bytes() + sizeof(Reply));
125 std::copy(data.begin(), data.end(), replyBuf.data());
126 auto* reply = reinterpret_cast<Reply*>(replyBuf.data() + data.size_bytes());
127 reply->nameLength = name.length();
128 memcpy(reply->name, name.c_str(), reply->nameLength + 1);
129
130 return ::ipmi::responseSuccess(SysOEMCommands::SysAccelOobDeviceName,
131 replyBuf);
132 }
133
134 namespace
135 {
136
137 struct NameHeader
138 {
139 uint8_t nameLength;
140 char name[MAX_NAME_SIZE];
141 } __attribute__((packed));
142
143 // Reads the variable-length name from reqBuf and outputs the name and a pointer
144 // to the payload (next byte after name).
145 //
146 // Returns: =0: success.
147 // >0: if dataLen is too small, returns the minimum buffers size.
148 //
149 // Params:
150 // [in] reqBuf - the request buffer
151 // [in] dataLen - the length of reqBuf, in bytes
152 // [in] payloadSize - the size of the expected payload
153 // [out] name - the name string
154 // [out] payload - pointer into reqBuf just after name
ReadNameHeader(const uint8_t * reqBuf,size_t dataLen,size_t payloadSize,std::string * name,const uint8_t ** payload)155 size_t ReadNameHeader(const uint8_t* reqBuf, size_t dataLen, size_t payloadSize,
156 std::string* name, const uint8_t** payload)
157 {
158 constexpr size_t kNameHeaderSize = sizeof(NameHeader) - MAX_NAME_SIZE;
159
160 auto* req_header = reinterpret_cast<const NameHeader*>(reqBuf);
161
162 size_t minDataLen = kNameHeaderSize + payloadSize + req_header->nameLength;
163 if (dataLen < minDataLen)
164 {
165 return minDataLen;
166 }
167
168 if (name)
169 {
170 *name = std::string(req_header->name, req_header->nameLength);
171 }
172 if (payload)
173 {
174 *payload = reqBuf + kNameHeaderSize + req_header->nameLength;
175 }
176 return 0;
177 }
178
179 } // namespace
180
accelOobRead(std::span<const uint8_t> data,HandlerInterface * handler)181 Resp accelOobRead(std::span<const uint8_t> data, HandlerInterface* handler)
182 {
183 struct Request
184 {
185 // Variable length header, handled by ReadNameHeader
186 // uint8_t nameLength; // <= MAX_NAME_SIZE
187 // char name[nameLength];
188
189 // Additional arguments
190 uint8_t token;
191 uint64_t address;
192 uint8_t num_bytes;
193 } __attribute__((packed));
194
195 struct Reply
196 {
197 uint64_t data;
198 } __attribute__((packed));
199
200 stdplus::print(stderr,
201 "AccelOob Read command sizes: "
202 "command={}B, payload={}B, max={}B\n",
203 data.size_bytes(), sizeof(Reply), MAX_IPMI_BUFFER);
204
205 std::string name;
206 const uint8_t* payload;
207
208 size_t min_size = ReadNameHeader(data.data(), data.size_bytes(),
209 sizeof(Request), &name, &payload);
210 if (min_size != 0)
211 {
212 stdplus::print(stderr, "AccelOob Read command too small: {}B < {}B\n",
213 data.size_bytes(), min_size);
214 return ::ipmi::responseReqDataLenInvalid();
215 }
216
217 if (data.size_bytes() + sizeof(Reply) > MAX_IPMI_BUFFER)
218 {
219 stdplus::print(stderr,
220 "AccelOob Read command too large for reply buffer: "
221 "command={}B, payload={}B, max={}B\n",
222 data.size_bytes(), sizeof(Reply), MAX_IPMI_BUFFER);
223 return ::ipmi::responseReqDataLenExceeded();
224 }
225
226 auto req = reinterpret_cast<const Request*>(payload);
227 uint64_t r = handler->accelOobRead(name, req->address, req->num_bytes);
228
229 std::vector<uint8_t> replyBuf(data.size_bytes() + sizeof(Reply));
230 std::copy(data.begin(), data.end(), replyBuf.data());
231 auto* reply = reinterpret_cast<Reply*>(replyBuf.data() + data.size_bytes());
232 reply->data = r;
233
234 return ::ipmi::responseSuccess(SysOEMCommands::SysAccelOobRead, replyBuf);
235 }
236
accelOobWrite(std::span<const uint8_t> data,HandlerInterface * handler)237 Resp accelOobWrite(std::span<const uint8_t> data, HandlerInterface* handler)
238 {
239 struct Request
240 {
241 // Variable length header, handled by ReadNameHeader
242 // uint8_t nameLength; // <= MAX_NAME_SIZE
243 // char name[nameLength];
244
245 // Additional arguments
246 uint8_t token;
247 uint64_t address;
248 uint8_t num_bytes;
249 uint64_t data;
250 } __attribute__((packed));
251
252 struct Reply
253 {
254 // Empty
255 } __attribute__((packed));
256
257 std::string name{};
258 const uint8_t* payload;
259
260 size_t min_size = ReadNameHeader(data.data(), data.size_bytes(),
261 sizeof(Request), &name, &payload);
262 if (min_size != 0)
263 {
264 stdplus::print(stderr, "AccelOob Write command too small: {}B < {}B\n",
265 data.size_bytes(), min_size);
266 return ::ipmi::responseReqDataLenInvalid();
267 }
268
269 if (data.size_bytes() + sizeof(Reply) > MAX_IPMI_BUFFER)
270 {
271 stdplus::print(stderr,
272 "AccelOob Write command too large for reply buffer: "
273 "command={}B, payload={}B, max={}B\n",
274 data.size_bytes(), sizeof(Reply), MAX_IPMI_BUFFER);
275 return ::ipmi::responseReqDataLenExceeded();
276 }
277
278 auto req = reinterpret_cast<const Request*>(payload);
279 handler->accelOobWrite(name, req->address, req->num_bytes, req->data);
280
281 std::vector<uint8_t> replyBuf(data.size_bytes() + sizeof(Reply));
282 std::copy(data.begin(), data.end(), replyBuf.data());
283
284 return ::ipmi::responseSuccess(SysOEMCommands::SysAccelOobWrite, replyBuf);
285 }
286
accelGetVrSettings(::ipmi::Context::ptr ctx,std::span<const uint8_t> data,HandlerInterface * handler)287 Resp accelGetVrSettings(::ipmi::Context::ptr ctx, std::span<const uint8_t> data,
288 HandlerInterface* handler)
289 {
290 uint16_t value;
291 if (data.size_bytes() != 2)
292 {
293 stdplus::println(
294 stderr,
295 "accelGetVrSettings command has incorrect size: %zuB != %dB\n",
296 data.size_bytes(), 2);
297 return ::ipmi::responseReqDataLenInvalid();
298 }
299
300 try
301 {
302 value = handler->accelGetVrSettings(ctx, /*chip_id*/ data[0],
303 /*settings_id*/ data[1]);
304 }
305 catch (const IpmiException& e)
306 {
307 return ::ipmi::response(e.getIpmiError());
308 }
309 return ::ipmi::responseSuccess(
310 SysOEMCommands::SysGetAccelVrSettings,
311 std::vector<uint8_t>{static_cast<uint8_t>(value),
312 static_cast<uint8_t>(value >> 8)});
313 }
314
accelSetVrSettings(::ipmi::Context::ptr ctx,std::span<const uint8_t> data,HandlerInterface * handler)315 Resp accelSetVrSettings(::ipmi::Context::ptr ctx, std::span<const uint8_t> data,
316 HandlerInterface* handler)
317 {
318 if (data.size_bytes() != 4)
319 {
320 stdplus::println(
321 stderr,
322 "accelSetVrSettings command has incorrect size: %zuB != %dB\n",
323 data.size_bytes(), 4);
324 return ::ipmi::responseReqDataLenInvalid();
325 }
326 uint16_t value = static_cast<uint16_t>(data[2] | data[3] << 8);
327 try
328 {
329 handler->accelSetVrSettings(ctx, /*chip_id*/ data[0],
330 /*settings_id*/ data[1], /*value*/ value);
331 }
332 catch (const IpmiException& e)
333 {
334 return ::ipmi::response(e.getIpmiError());
335 }
336 return ::ipmi::responseSuccess(SysOEMCommands::SysSetAccelVrSettings,
337 std::vector<uint8_t>{});
338 }
339 } // namespace ipmi
340 } // namespace google
341