xref: /openbmc/google-ipmi-sys/google_accel_oob.cpp (revision d455bfd6e08dd09d10b220adc99d29ac33839e59)
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