blob: e2e0feff90f0325041f848e77273ea972a51c393 [file] [log] [blame]
Patrick Ventureef3aead2018-09-12 08:53:29 -07001/*
2 * Copyright 2018 Google Inc.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "ipmi.hpp"
18
19#include <cstring>
20#include <string>
21#include <unordered_map>
22
23namespace blobs
24{
25
26bool validateRequestLength(BlobOEMCommands command, size_t requestLen)
27{
28 /* The smallest string is one letter and the nul-terminator. */
29 static const int kMinStrLen = 2;
30
31 static const std::unordered_map<BlobOEMCommands, size_t> minimumLengths = {
32 {BlobOEMCommands::bmcBlobEnumerate, sizeof(struct BmcBlobEnumerateTx)},
33 {BlobOEMCommands::bmcBlobOpen,
34 sizeof(struct BmcBlobOpenTx) + kMinStrLen},
35 {BlobOEMCommands::bmcBlobClose, sizeof(struct BmcBlobCloseTx)},
36 {BlobOEMCommands::bmcBlobDelete,
37 sizeof(struct BmcBlobDeleteTx) + kMinStrLen},
38 {BlobOEMCommands::bmcBlobStat,
39 sizeof(struct BmcBlobStatTx) + kMinStrLen},
40 {BlobOEMCommands::bmcBlobSessionStat,
41 sizeof(struct BmcBlobSessionStatTx)},
42 {BlobOEMCommands::bmcBlobCommit, sizeof(struct BmcBlobCommitTx)},
43 {BlobOEMCommands::bmcBlobRead, sizeof(struct BmcBlobReadTx)},
44 {BlobOEMCommands::bmcBlobWrite,
45 sizeof(struct BmcBlobWriteTx) + sizeof(uint8_t)},
Patrick Venture5c4b17b2018-10-04 10:32:22 -070046 {BlobOEMCommands::bmcBlobWriteMeta,
47 sizeof(struct BmcBlobWriteMetaTx) + sizeof(uint8_t)},
Patrick Ventureef3aead2018-09-12 08:53:29 -070048 };
49
50 auto results = minimumLengths.find(command);
51 if (results == minimumLengths.end())
52 {
53 /* Valid length by default if we don't care. */
54 return true;
55 }
56
57 /* If the request is shorter than the minimum, it's invalid. */
58 if (requestLen < results->second)
59 {
60 return false;
61 }
62
63 return true;
64}
65
66std::string stringFromBuffer(const char* start, size_t length)
67{
68 if (!start)
69 {
70 return "";
71 }
72
73 auto end = static_cast<const char*>(std::memchr(start, '\0', length));
74 if (end != &start[length - 1])
75 {
76 return "";
77 }
78
79 return (end == nullptr) ? std::string() : std::string(start, end);
80}
81
82ipmi_ret_t getBlobCount(ManagerInterface* mgr, const uint8_t* reqBuf,
83 uint8_t* replyCmdBuf, size_t* dataLen)
84{
85 struct BmcBlobCountRx resp;
86 resp.crc = 0;
87 resp.blobCount = mgr->buildBlobList();
88
89 /* Copy the response into the reply buffer */
90 std::memcpy(replyCmdBuf, &resp, sizeof(resp));
91 (*dataLen) = sizeof(resp);
92
93 return IPMI_CC_OK;
94}
95
96ipmi_ret_t enumerateBlob(ManagerInterface* mgr, const uint8_t* reqBuf,
97 uint8_t* replyCmdBuf, size_t* dataLen)
98{
99 /* Verify datalen is >= sizeof(request) */
100 struct BmcBlobEnumerateTx request;
101 auto reply = reinterpret_cast<struct BmcBlobEnumerateRx*>(replyCmdBuf);
102
103 std::memcpy(&request, reqBuf, sizeof(request));
104
105 std::string blobId = mgr->getBlobId(request.blobIdx);
106 if (blobId == "")
107 {
Patrick Venture41258802018-11-12 10:46:30 -0800108 return IPMI_CC_INVALID_FIELD_REQUEST;
Patrick Ventureef3aead2018-09-12 08:53:29 -0700109 }
110
111 /* TODO(venture): Need to do a hard-code check against the maximum
112 * reply buffer size. */
113 reply->crc = 0;
114 /* Explicilty copies the NUL-terminator. */
115 std::memcpy(&reply->blobId, blobId.c_str(), blobId.length() + 1);
116
117 (*dataLen) = sizeof(reply->crc) + blobId.length() + 1;
118
119 return IPMI_CC_OK;
120}
121
122ipmi_ret_t openBlob(ManagerInterface* mgr, const uint8_t* reqBuf,
123 uint8_t* replyCmdBuf, size_t* dataLen)
124{
125 size_t requestLen = (*dataLen);
126 auto request = reinterpret_cast<const struct BmcBlobOpenTx*>(reqBuf);
127 uint16_t session;
128
129 std::string path = stringFromBuffer(
130 request->blobId, (requestLen - sizeof(struct BmcBlobOpenTx)));
131 if (path.empty())
132 {
Patrick Venture41258802018-11-12 10:46:30 -0800133 return IPMI_CC_REQ_DATA_LEN_INVALID;
Patrick Ventureef3aead2018-09-12 08:53:29 -0700134 }
135
136 /* Attempt to open. */
137 if (!mgr->open(request->flags, path, &session))
138 {
Patrick Venture41258802018-11-12 10:46:30 -0800139 return IPMI_CC_UNSPECIFIED_ERROR;
Patrick Ventureef3aead2018-09-12 08:53:29 -0700140 }
141
142 struct BmcBlobOpenRx reply;
143 reply.crc = 0;
144 reply.sessionId = session;
145
146 std::memcpy(replyCmdBuf, &reply, sizeof(reply));
147 (*dataLen) = sizeof(reply);
148
149 return IPMI_CC_OK;
150}
151
152ipmi_ret_t closeBlob(ManagerInterface* mgr, const uint8_t* reqBuf,
153 uint8_t* replyCmdBuf, size_t* dataLen)
154{
155 struct BmcBlobCloseTx request;
156 std::memcpy(&request, reqBuf, sizeof(request));
157
158 /* Attempt to close. */
159 if (!mgr->close(request.sessionId))
160 {
Patrick Venture41258802018-11-12 10:46:30 -0800161 return IPMI_CC_UNSPECIFIED_ERROR;
Patrick Ventureef3aead2018-09-12 08:53:29 -0700162 }
163
164 (*dataLen) = 0;
165 return IPMI_CC_OK;
166}
167
168ipmi_ret_t deleteBlob(ManagerInterface* mgr, const uint8_t* reqBuf,
169 uint8_t* replyCmdBuf, size_t* dataLen)
170{
171 size_t requestLen = (*dataLen);
172 auto request = reinterpret_cast<const struct BmcBlobDeleteTx*>(reqBuf);
173
174 std::string path = stringFromBuffer(
175 request->blobId, (requestLen - sizeof(struct BmcBlobDeleteTx)));
176 if (path.empty())
177 {
Patrick Venture41258802018-11-12 10:46:30 -0800178 return IPMI_CC_REQ_DATA_LEN_INVALID;
Patrick Ventureef3aead2018-09-12 08:53:29 -0700179 }
180
181 /* Attempt to delete. */
182 if (!mgr->deleteBlob(path))
183 {
Patrick Venture41258802018-11-12 10:46:30 -0800184 return IPMI_CC_UNSPECIFIED_ERROR;
Patrick Ventureef3aead2018-09-12 08:53:29 -0700185 }
186
187 (*dataLen) = 0;
188 return IPMI_CC_OK;
189}
190
191static ipmi_ret_t returnStatBlob(struct BlobMeta* meta, uint8_t* replyCmdBuf,
192 size_t* dataLen)
193{
194 struct BmcBlobStatRx reply;
195 reply.crc = 0;
196 reply.blobState = meta->blobState;
197 reply.size = meta->size;
198 reply.metadataLen = meta->metadata.size();
199
200 std::memcpy(replyCmdBuf, &reply, sizeof(reply));
201
202 /* If there is metadata, copy it over. */
203 if (meta->metadata.size())
204 {
205 uint8_t* metadata = &replyCmdBuf[sizeof(reply)];
206 std::memcpy(metadata, meta->metadata.data(), reply.metadataLen);
207 }
208
209 (*dataLen) = sizeof(reply) + reply.metadataLen;
210 return IPMI_CC_OK;
211}
212
213ipmi_ret_t statBlob(ManagerInterface* mgr, const uint8_t* reqBuf,
214 uint8_t* replyCmdBuf, size_t* dataLen)
215{
216 size_t requestLen = (*dataLen);
217 auto request = reinterpret_cast<const struct BmcBlobStatTx*>(reqBuf);
218
219 std::string path = stringFromBuffer(
220 request->blobId, (requestLen - sizeof(struct BmcBlobStatTx)));
221 if (path.empty())
222 {
Patrick Venture41258802018-11-12 10:46:30 -0800223 return IPMI_CC_REQ_DATA_LEN_INVALID;
Patrick Ventureef3aead2018-09-12 08:53:29 -0700224 }
225
226 /* Attempt to stat. */
227 struct BlobMeta meta;
228 if (!mgr->stat(path, &meta))
229 {
Patrick Venture41258802018-11-12 10:46:30 -0800230 return IPMI_CC_UNSPECIFIED_ERROR;
Patrick Ventureef3aead2018-09-12 08:53:29 -0700231 }
232
233 return returnStatBlob(&meta, replyCmdBuf, dataLen);
234}
235
236ipmi_ret_t sessionStatBlob(ManagerInterface* mgr, const uint8_t* reqBuf,
237 uint8_t* replyCmdBuf, size_t* dataLen)
238{
239 struct BmcBlobSessionStatTx request;
240 std::memcpy(&request, reqBuf, sizeof(request));
241
242 /* Attempt to stat. */
243 struct BlobMeta meta;
244
245 if (!mgr->stat(request.sessionId, &meta))
246 {
Patrick Venture41258802018-11-12 10:46:30 -0800247 return IPMI_CC_UNSPECIFIED_ERROR;
Patrick Ventureef3aead2018-09-12 08:53:29 -0700248 }
249
250 return returnStatBlob(&meta, replyCmdBuf, dataLen);
251}
252
253ipmi_ret_t commitBlob(ManagerInterface* mgr, const uint8_t* reqBuf,
254 uint8_t* replyCmdBuf, size_t* dataLen)
255{
256 size_t requestLen = (*dataLen);
257 auto request = reinterpret_cast<const struct BmcBlobCommitTx*>(reqBuf);
258
259 /* Sanity check the commitDataLen */
260 if (request->commitDataLen > (requestLen - sizeof(struct BmcBlobCommitTx)))
261 {
Patrick Venture41258802018-11-12 10:46:30 -0800262 return IPMI_CC_REQ_DATA_LEN_INVALID;
Patrick Ventureef3aead2018-09-12 08:53:29 -0700263 }
264
265 std::vector<uint8_t> data(request->commitDataLen);
266 std::memcpy(data.data(), request->commitData, request->commitDataLen);
267
268 if (!mgr->commit(request->sessionId, data))
269 {
Patrick Venture41258802018-11-12 10:46:30 -0800270 return IPMI_CC_UNSPECIFIED_ERROR;
Patrick Ventureef3aead2018-09-12 08:53:29 -0700271 }
272
273 (*dataLen) = 0;
274 return IPMI_CC_OK;
275}
276
277ipmi_ret_t readBlob(ManagerInterface* mgr, const uint8_t* reqBuf,
278 uint8_t* replyCmdBuf, size_t* dataLen)
279{
280 struct BmcBlobReadTx request;
281 std::memcpy(&request, reqBuf, sizeof(request));
282
283 /* TODO(venture): Verify requestedSize can fit in a returned IPMI packet.
284 */
285
286 std::vector<uint8_t> result =
287 mgr->read(request.sessionId, request.offset, request.requestedSize);
288
289 /* If the Read fails, it returns success but with only the crc and 0 bytes
290 * of data.
291 * If there was data returned, copy into the reply buffer.
292 */
293 (*dataLen) = sizeof(struct BmcBlobReadRx);
294
295 if (result.size())
296 {
297 uint8_t* output = &replyCmdBuf[sizeof(struct BmcBlobReadRx)];
298 std::memcpy(output, result.data(), result.size());
299
300 (*dataLen) = sizeof(struct BmcBlobReadRx) + result.size();
301 }
302
303 return IPMI_CC_OK;
304}
305
306ipmi_ret_t writeBlob(ManagerInterface* mgr, const uint8_t* reqBuf,
307 uint8_t* replyCmdBuf, size_t* dataLen)
308{
309 size_t requestLen = (*dataLen);
310 auto request = reinterpret_cast<const struct BmcBlobWriteTx*>(reqBuf);
311
312 uint32_t size = requestLen - sizeof(struct BmcBlobWriteTx);
313 std::vector<uint8_t> data(size);
314
315 std::memcpy(data.data(), request->data, size);
316
317 /* Attempt to write the bytes. */
318 if (!mgr->write(request->sessionId, request->offset, data))
319 {
Patrick Venture41258802018-11-12 10:46:30 -0800320 return IPMI_CC_UNSPECIFIED_ERROR;
Patrick Ventureef3aead2018-09-12 08:53:29 -0700321 }
322
323 return IPMI_CC_OK;
324}
325
Patrick Venture5c4b17b2018-10-04 10:32:22 -0700326ipmi_ret_t writeMeta(ManagerInterface* mgr, const uint8_t* reqBuf,
327 uint8_t* replyCmdBuf, size_t* dataLen)
328{
329 size_t requestLen = (*dataLen);
330 struct BmcBlobWriteMetaTx request;
331
332 /* Copy over the request. */
333 std::memcpy(&request, reqBuf, sizeof(request));
334
335 /* Determine number of bytes of metadata to write. */
336 uint32_t size = requestLen - sizeof(request);
337
338 /* Nothing really else to validate, we just copy those bytes. */
339 std::vector<uint8_t> data(size);
340 std::memcpy(data.data(), &reqBuf[sizeof(request)], size);
341
342 /* Attempt to write the bytes. */
343 if (!mgr->writeMeta(request.sessionId, request.offset, data))
344 {
Patrick Venture41258802018-11-12 10:46:30 -0800345 return IPMI_CC_UNSPECIFIED_ERROR;
Patrick Venture5c4b17b2018-10-04 10:32:22 -0700346 }
347
348 return IPMI_CC_OK;
349}
350
Patrick Ventureef3aead2018-09-12 08:53:29 -0700351} // namespace blobs