xref: /openbmc/google-ipmi-sys/google_accel_oob.cpp (revision 444b5ea4847ea7c014114094d4b63672122f9786)
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 
19 #include <sdbusplus/bus.hpp>
20 
21 #include <cstdint>
22 #include <cstring>
23 #include <span>
24 #include <string>
25 #include <vector>
26 
27 namespace google
28 {
29 namespace 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
37 constexpr size_t MAX_NAME_SIZE = MAX_IPMI_BUFFER - 1 - 8 - 1 - 8 - 1 - 1;
38 
39 Resp 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 
77 Resp 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 
129 namespace
130 {
131 
132 struct 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
150 size_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 
176 Resp 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 
232 Resp 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
284