blob: 0c2ffadd395707e8615e646ab225a077269edd60 [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"
Gaurav Gandhid455bfd2024-01-29 22:32:27 -080018#include "errors.hpp"
19#include "handler.hpp"
Steve Foreman4f0d1de2021-09-20 14:06:32 -070020
Patrick Williams444b5ea2023-05-19 13:56:42 -050021#include <sdbusplus/bus.hpp>
Michael Shen8d618532023-10-25 09:14:07 +000022#include <stdplus/print.hpp>
Patrick Williams444b5ea2023-05-19 13:56:42 -050023
Steve Foreman4f0d1de2021-09-20 14:06:32 -070024#include <cstdint>
25#include <cstring>
Steve Foreman4f0d1de2021-09-20 14:06:32 -070026#include <span>
27#include <string>
28#include <vector>
29
30namespace google
31{
32namespace 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
40constexpr size_t MAX_NAME_SIZE = MAX_IPMI_BUFFER - 1 - 8 - 1 - 8 - 1 - 1;
41
42Resp 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 {
Michael Shen8d618532023-10-25 09:14:07 +000056 stdplus::print(stderr, "AccelOob DeviceCount command too small: {}\n",
57 data.size_bytes());
Steve Foreman4f0d1de2021-09-20 14:06:32 -070058 return ::ipmi::responseReqDataLenInvalid();
59 }
60
61 if (data.size_bytes() + sizeof(Reply) > MAX_IPMI_BUFFER)
62 {
Michael Shen8d618532023-10-25 09:14:07 +000063 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);
Steve Foreman4f0d1de2021-09-20 14:06:32 -070068 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
81Resp 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 {
Michael Shen8d618532023-10-25 09:14:07 +000097 stdplus::print(stderr, "AccelOob DeviceName command too small: {}\n",
98 data.size_bytes());
Steve Foreman4f0d1de2021-09-20 14:06:32 -070099 return ::ipmi::responseReqDataLenInvalid();
100 }
101
102 if (data.size_bytes() + sizeof(Reply) > MAX_IPMI_BUFFER)
103 {
Michael Shen8d618532023-10-25 09:14:07 +0000104 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);
Steve Foreman4f0d1de2021-09-20 14:06:32 -0700109 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 {
Michael Shen8d618532023-10-25 09:14:07 +0000117 stdplus::print(stderr,
118 "AccelOob: name was too long. "
119 "'{}' len must be <= {}\n",
120 name, MAX_NAME_SIZE);
Steve Foreman4f0d1de2021-09-20 14:06:32 -0700121 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
134namespace
135{
136
137struct 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
155size_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
181Resp 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
Michael Shen8d618532023-10-25 09:14:07 +0000200 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);
Steve Foreman4f0d1de2021-09-20 14:06:32 -0700204
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 {
Michael Shen8d618532023-10-25 09:14:07 +0000212 stdplus::print(stderr, "AccelOob Read command too small: {}B < {}B\n",
213 data.size_bytes(), min_size);
Steve Foreman4f0d1de2021-09-20 14:06:32 -0700214 return ::ipmi::responseReqDataLenInvalid();
215 }
216
217 if (data.size_bytes() + sizeof(Reply) > MAX_IPMI_BUFFER)
218 {
Michael Shen8d618532023-10-25 09:14:07 +0000219 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);
Steve Foreman4f0d1de2021-09-20 14:06:32 -0700223 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
237Resp 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 {
Michael Shen8d618532023-10-25 09:14:07 +0000264 stdplus::print(stderr, "AccelOob Write command too small: {}B < {}B\n",
265 data.size_bytes(), min_size);
Steve Foreman4f0d1de2021-09-20 14:06:32 -0700266 return ::ipmi::responseReqDataLenInvalid();
267 }
268
269 if (data.size_bytes() + sizeof(Reply) > MAX_IPMI_BUFFER)
270 {
Michael Shen8d618532023-10-25 09:14:07 +0000271 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);
Steve Foreman4f0d1de2021-09-20 14:06:32 -0700275 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
Gaurav Gandhid455bfd2024-01-29 22:32:27 -0800287Resp 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
315Resp 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}
Steve Foreman4f0d1de2021-09-20 14:06:32 -0700339} // namespace ipmi
340} // namespace google