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