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