blob: 9cc88795a6a3fb8cf11e59cfc69fb1cc76cb6749 [file] [log] [blame]
Vernon Mauerya3702c12019-05-22 13:20:59 -07001/*
2// Copyright (c) 2018 Intel Corporation
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
Vernon Mauerya3702c12019-05-22 13:20:59 -070017#include <bridgingcommands.hpp>
18#include <cstring>
Vernon Mauery15419dd2019-05-24 09:40:30 -070019#include <ipmid/api.hpp>
Vernon Mauerya3702c12019-05-22 13:20:59 -070020#include <phosphor-logging/log.hpp>
21#include <sdbusplus/bus.hpp>
22#include <sdbusplus/bus/match.hpp>
23#include <sdbusplus/message.hpp>
24#include <vector>
25
26static constexpr const char *ipmbBus = "xyz.openbmc_project.Ipmi.Channel.Ipmb";
27static constexpr const char *ipmbObj = "/xyz/openbmc_project/Ipmi/Channel/Ipmb";
28static constexpr const char *ipmbIntf = "org.openbmc.Ipmb";
29
30static Bridging bridging;
31
32/**
33 * @brief utils for checksum
34 */
35static bool ipmbChecksumValidate(uint8_t *data, uint8_t length)
36{
37 if (data == nullptr)
38 {
39 return false;
40 }
41
42 uint8_t checksum = 0;
43
44 for (uint8_t idx = 0; idx < length; idx++)
45 {
46 checksum += data[idx];
47 }
48
49 if (0 == checksum)
50 {
51 return true;
52 }
53
54 return false;
55}
56
57static uint8_t ipmbChecksumCompute(uint8_t *data, uint8_t length)
58{
59 if (data == nullptr)
60 {
61 return 0;
62 }
63
64 uint8_t checksum = 0;
65
66 for (uint8_t idx = 0; idx < length; idx++)
67 {
68 checksum += data[idx];
69 }
70
71 checksum = (~checksum) + 1;
72 return checksum;
73}
74
75static inline bool ipmbConnectionHeaderChecksumValidate(ipmbHeader *ipmbHeader)
76{
77 return ipmbChecksumValidate(reinterpret_cast<uint8_t *>(ipmbHeader),
78 ipmbConnectionHeaderLength);
79}
80
81static inline bool ipmbDataChecksumValidate(ipmbHeader *ipmbHeader,
82 uint8_t length)
83{
84 return ipmbChecksumValidate(
85 (reinterpret_cast<uint8_t *>(ipmbHeader) + ipmbConnectionHeaderLength),
86 (length - ipmbConnectionHeaderLength));
87}
88
89static bool isFrameValid(ipmbHeader *frame, uint8_t length)
90{
91 if ((length < ipmbMinFrameLength) || (length > ipmbMaxFrameLength))
92 {
93 return false;
94 }
95
96 if (false == ipmbConnectionHeaderChecksumValidate(frame))
97 {
98 return false;
99 }
100
101 if (false == ipmbDataChecksumValidate(frame, length))
102 {
103 return false;
104 }
105
106 return true;
107}
108
109IpmbRequest::IpmbRequest(const ipmbHeader *ipmbBuffer, size_t bufferLength)
110{
111 address = ipmbBuffer->Header.Req.address;
112 netFn = ipmbNetFnGet(ipmbBuffer->Header.Req.rsNetFnLUN);
113 rsLun = ipmbLunFromNetFnLunGet(ipmbBuffer->Header.Req.rsNetFnLUN);
114 rqSA = ipmbBuffer->Header.Req.rqSA;
115 seq = ipmbSeqGet(ipmbBuffer->Header.Req.rqSeqLUN);
116 rqLun = ipmbLunFromSeqLunGet(ipmbBuffer->Header.Req.rqSeqLUN);
117 cmd = ipmbBuffer->Header.Req.cmd;
118
119 size_t dataLength =
120 bufferLength - (ipmbConnectionHeaderLength +
121 ipmbRequestDataHeaderLength + ipmbChecksumSize);
122
123 if (dataLength > 0)
124 {
125 data.insert(data.end(), ipmbBuffer->Header.Req.data,
126 &ipmbBuffer->Header.Req.data[dataLength]);
127 }
128}
129
130IpmbResponse::IpmbResponse(uint8_t address, uint8_t netFn, uint8_t rqLun,
131 uint8_t rsSA, uint8_t seq, uint8_t rsLun,
132 uint8_t cmd, uint8_t completionCode,
133 std::vector<uint8_t> &inputData) :
134 address(address),
135 netFn(netFn), rqLun(rqLun), rsSA(rsSA), seq(seq), rsLun(rsLun), cmd(cmd),
136 completionCode(completionCode)
137{
138 data.reserve(ipmbMaxDataSize);
139
140 if (inputData.size() > 0)
141 {
142 data = std::move(inputData);
143 }
144}
145
146void IpmbResponse::ipmbToi2cConstruct(uint8_t *buffer, size_t *bufferLength)
147{
148 ipmbHeader *ipmbBuffer = (ipmbHeader *)buffer;
149
150 ipmbBuffer->Header.Resp.address = address;
151 ipmbBuffer->Header.Resp.rqNetFnLUN = ipmbNetFnLunSet(netFn, rqLun);
152 ipmbBuffer->Header.Resp.rsSA = rsSA;
153 ipmbBuffer->Header.Resp.rsSeqLUN = ipmbSeqLunSet(seq, rsLun);
154 ipmbBuffer->Header.Resp.cmd = cmd;
155 ipmbBuffer->Header.Resp.completionCode = completionCode;
156
157 ipmbBuffer->Header.Resp.checksum1 = ipmbChecksumCompute(
158 buffer, ipmbConnectionHeaderLength - ipmbChecksumSize);
159
160 if (data.size() > 0)
161 {
162 std::copy(
163 data.begin(), data.end(),
164 &buffer[ipmbConnectionHeaderLength + ipmbResponseDataHeaderLength]);
165 }
166
167 *bufferLength = data.size() + ipmbResponseDataHeaderLength +
168 ipmbConnectionHeaderLength + ipmbChecksumSize;
169
170 buffer[*bufferLength - ipmbChecksumSize] =
171 ipmbChecksumCompute(&buffer[ipmbChecksum2StartOffset],
172 (ipmbResponseDataHeaderLength + data.size()));
173}
174
175void IpmbRequest::prepareRequest(sdbusplus::message::message &mesg)
176{
177 mesg.append(ipmbMeChannelNum, netFn, rqLun, cmd, data);
178}
179
Vernon Mauerya3702c12019-05-22 13:20:59 -0700180ipmi_return_codes Bridging::handleIpmbChannel(sSendMessageReq *sendMsgReq,
181 ipmi_response_t response,
182 ipmi_data_len_t dataLen)
183{
184 if ((*dataLen < (sizeof(sSendMessageReq) + ipmbMinFrameLength)) ||
185 (*dataLen > (sizeof(sSendMessageReq) + ipmbMaxFrameLength)))
186 {
187 *dataLen = 0;
188 return IPMI_CC_REQ_DATA_LEN_INVALID;
189 }
190
191 auto sendMsgReqData = reinterpret_cast<ipmbHeader *>(sendMsgReq->data);
192
193 // TODO: check privilege lvl. Bridging to ME requires Administrator lvl
194
195 // allow bridging to ME only
196 if (sendMsgReqData->Header.Req.address != ipmbMeSlaveAddress)
197 {
198 phosphor::logging::log<phosphor::logging::level::INFO>(
199 "handleIpmbChannel, IPMB address invalid");
200 *dataLen = 0;
201 return IPMI_CC_PARM_OUT_OF_RANGE;
202 }
203
204 // check allowed modes
205 if (sendMsgReq->modeGet() != modeNoTracking &&
206 sendMsgReq->modeGet() != modeTrackRequest)
207 {
208 phosphor::logging::log<phosphor::logging::level::INFO>(
209 "handleIpmbChannel, mode not supported");
210 *dataLen = 0;
211 return IPMI_CC_PARM_OUT_OF_RANGE;
212 }
213
214 // check if request contains valid IPMB frame
215 if (!isFrameValid(sendMsgReqData, (*dataLen - sizeof(sSendMessageReq))))
216 {
217 phosphor::logging::log<phosphor::logging::level::INFO>(
218 "handleIpmbChannel, IPMB frame invalid");
219 *dataLen = 0;
220 return IPMI_CC_PARM_OUT_OF_RANGE;
221 }
222
223 auto ipmbRequest =
224 IpmbRequest(sendMsgReqData, (*dataLen - sizeof(sSendMessageReq)));
225
226 std::tuple<int, uint8_t, uint8_t, uint8_t, uint8_t, std::vector<uint8_t>>
227 ipmbResponse;
228
229 // send request to IPMB
230 try
231 {
Vernon Mauery15419dd2019-05-24 09:40:30 -0700232 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
Vernon Mauerya3702c12019-05-22 13:20:59 -0700233 auto mesg =
Vernon Mauery15419dd2019-05-24 09:40:30 -0700234 dbus->new_method_call(ipmbBus, ipmbObj, ipmbIntf, "sendRequest");
Vernon Mauerya3702c12019-05-22 13:20:59 -0700235 ipmbRequest.prepareRequest(mesg);
Vernon Mauery15419dd2019-05-24 09:40:30 -0700236 auto ret = dbus->call(mesg);
Vernon Mauerya3702c12019-05-22 13:20:59 -0700237 ret.read(ipmbResponse);
238 }
239 catch (sdbusplus::exception::SdBusError &e)
240 {
241 phosphor::logging::log<phosphor::logging::level::ERR>(
242 "handleIpmbChannel, dbus call exception");
243 *dataLen = 0;
244 return IPMI_CC_UNSPECIFIED_ERROR;
245 }
246
247 std::vector<uint8_t> dataReceived(0);
248 int status = -1;
249 uint8_t netFn = 0, lun = 0, cmd = 0, cc = 0;
250
251 std::tie(status, netFn, lun, cmd, cc, dataReceived) = ipmbResponse;
252
253 auto respReceived =
254 IpmbResponse(ipmbRequest.rqSA, netFn, lun, ipmbRequest.address,
255 ipmbRequest.seq, lun, cmd, cc, dataReceived);
256
257 // check IPMB layer status
258 if (status)
259 {
260 phosphor::logging::log<phosphor::logging::level::WARNING>(
261 "handleIpmbChannel, ipmb returned non zero status");
262 *dataLen = 0;
263 return IPMI_CC_RESPONSE_ERROR;
264 }
265
266 auto sendMsgRes = reinterpret_cast<uint8_t *>(response);
267
268 switch (sendMsgReq->modeGet())
269 {
270 case modeNoTracking:
271 if (responseQueue.size() == responseQueueMaxSize)
272 {
273 *dataLen = 0;
274 return IPMI_CC_BUSY;
275 }
276 responseQueue.insert(responseQueue.end(), std::move(respReceived));
277 *dataLen = 0;
278 return IPMI_CC_OK;
279
280 break;
281 case modeTrackRequest:
282 respReceived.ipmbToi2cConstruct(sendMsgRes, dataLen);
283 return IPMI_CC_OK;
284
285 break;
286 default:
287 phosphor::logging::log<phosphor::logging::level::INFO>(
288 "handleIpmbChannel, mode not supported");
289 *dataLen = 0;
290 return IPMI_CC_PARM_OUT_OF_RANGE;
291 }
292
293 *dataLen = 0;
294 return IPMI_CC_UNSPECIFIED_ERROR;
295}
296
297ipmi_return_codes Bridging::sendMessageHandler(ipmi_request_t request,
298 ipmi_response_t response,
299 ipmi_data_len_t dataLen)
300{
301 ipmi_return_codes retCode = IPMI_CC_OK;
302
303 if (*dataLen < sizeof(sSendMessageReq))
304 {
305 *dataLen = 0;
306 return IPMI_CC_REQ_DATA_LEN_INVALID;
307 }
308
309 auto sendMsgReq = reinterpret_cast<sSendMessageReq *>(request);
310
311 // check message fields:
312 // encryption not supported
313 if (sendMsgReq->encryptionGet() != 0)
314 {
315 phosphor::logging::log<phosphor::logging::level::INFO>(
316 "sendMessageHandler, encryption not supported");
317 *dataLen = 0;
318 return IPMI_CC_PARM_OUT_OF_RANGE;
319 }
320
321 // authentication not supported
322 if (sendMsgReq->authenticationGet() != 0)
323 {
324 phosphor::logging::log<phosphor::logging::level::INFO>(
325 "sendMessageHandler, authentication not supported");
326 *dataLen = 0;
327 return IPMI_CC_PARM_OUT_OF_RANGE;
328 }
329
330 switch (sendMsgReq->channelNumGet())
331 {
332 // we only handle ipmb for now
333 case targetChannelIpmb:
334 case targetChannelOtherLan:
335 retCode = handleIpmbChannel(sendMsgReq, response, dataLen);
336 break;
337 // fall through to default
338 case targetChannelIcmb10:
339 case targetChannelIcmb09:
340 case targetChannelLan:
341 case targetChannelSerialModem:
342 case targetChannelPciSmbus:
343 case targetChannelSmbus10:
344 case targetChannelSmbus20:
345 case targetChannelSystemInterface:
346 default:
347 phosphor::logging::log<phosphor::logging::level::INFO>(
348 "sendMessageHandler, TargetChannel invalid");
349 *dataLen = 0;
350 return IPMI_CC_PARM_OUT_OF_RANGE;
351 }
352
353 return retCode;
354}
355
356ipmi_return_codes Bridging::getMessageHandler(ipmi_request_t request,
357 ipmi_response_t response,
358 ipmi_data_len_t dataLen)
359{
360 if (*dataLen != 0)
361 {
362 *dataLen = 0;
363 return IPMI_CC_REQ_DATA_LEN_INVALID;
364 }
365
366 auto getMsgRes = reinterpret_cast<sGetMessageRes *>(response);
367 auto getMsgResData = static_cast<uint8_t *>(getMsgRes->data);
368
369 std::memset(getMsgRes, 0, sizeof(sGetMessageRes));
370
371 auto respQueueItem = responseQueue.begin();
372
373 if (respQueueItem == responseQueue.end())
374 {
375 phosphor::logging::log<phosphor::logging::level::INFO>(
376 "getMessageHandler, no data available");
377 *dataLen = 0;
378 return ipmiGetMessageCmdDataNotAvailable;
379 }
380
381 // set message fields
382 getMsgRes->privilegeLvlSet(SYSTEM_INTERFACE);
383 getMsgRes->channelNumSet(targetChannelSystemInterface);
384
385 // construct response
386 respQueueItem->ipmbToi2cConstruct(getMsgResData, dataLen);
387 responseQueue.erase(respQueueItem);
388
389 *dataLen = *dataLen + sizeof(sGetMessageRes);
390 return IPMI_CC_OK;
391}
392
393ipmi_return_codes Bridging::getMessageFlagsHandler(ipmi_request_t request,
394 ipmi_response_t response,
395 ipmi_data_len_t dataLen)
396{
397 if (*dataLen != 0)
398 {
399 *dataLen = 0;
400 return IPMI_CC_REQ_DATA_LEN_INVALID;
401 }
402
403 auto getMsgFlagsRes = reinterpret_cast<sGetMessageFlagsResp *>(response);
404
405 std::memset(getMsgFlagsRes, 0, sizeof(sGetMessageFlagsResp));
406
407 // preserve current (legacy) behaviour
408 getMsgFlagsRes->eventMessageBitSet(1);
409
410 // set message fields
411 if (responseQueue.size() > 0)
412 {
413 getMsgFlagsRes->receiveMessageBitSet(1);
414 }
415 else
416 {
417 getMsgFlagsRes->receiveMessageBitSet(0);
418 }
419
420 *dataLen = sizeof(sGetMessageFlagsResp);
421
422 return IPMI_CC_OK;
423}
424
425ipmi_return_codes Bridging::clearMessageFlagsHandler(ipmi_request_t request,
426 ipmi_response_t response,
427 ipmi_data_len_t dataLen)
428{
429 if (*dataLen != sizeof(sClearMessageFlagsReq))
430 {
431 return IPMI_CC_REQ_DATA_LEN_INVALID;
432 }
433
434 auto clearMsgFlagsReq = reinterpret_cast<sClearMessageFlagsReq *>(request);
435
436 if (clearMsgFlagsReq->receiveMessageBitGet() == 1)
437 {
438 responseQueue.clear();
439 }
440
441 return IPMI_CC_OK;
442}
443
444ipmi_ret_t ipmiAppSendMessage(ipmi_netfn_t netFn, ipmi_cmd_t cmd,
445 ipmi_request_t request, ipmi_response_t response,
446 ipmi_data_len_t dataLen, ipmi_context_t context)
447{
448 ipmi_ret_t retCode = IPMI_CC_OK;
449 retCode = bridging.sendMessageHandler(request, response, dataLen);
450
451 return retCode;
452}
453
454ipmi_ret_t ipmiAppGetMessage(ipmi_netfn_t netFn, ipmi_cmd_t cmd,
455 ipmi_request_t request, ipmi_response_t response,
456 ipmi_data_len_t dataLen, ipmi_context_t context)
457{
458 ipmi_ret_t retCode = IPMI_CC_OK;
459 retCode = bridging.getMessageHandler(request, response, dataLen);
460
461 return retCode;
462}
463
464ipmi_ret_t ipmiAppGetMessageFlags(ipmi_netfn_t netFn, ipmi_cmd_t cmd,
465 ipmi_request_t request,
466 ipmi_response_t response,
467 ipmi_data_len_t dataLen,
468 ipmi_context_t context)
469{
470 ipmi_ret_t retCode = IPMI_CC_OK;
471 retCode = bridging.getMessageFlagsHandler(request, response, dataLen);
472
473 return retCode;
474}
475
476ipmi_ret_t ipmiAppClearMessageFlags(ipmi_netfn_t netFn, ipmi_cmd_t cmd,
477 ipmi_request_t request,
478 ipmi_response_t response,
479 ipmi_data_len_t dataLen,
480 ipmi_context_t context)
481{
482 ipmi_ret_t retCode = IPMI_CC_OK;
483 retCode = bridging.clearMessageFlagsHandler(request, response, dataLen);
484
485 *dataLen = 0;
486
487 return retCode;
488}
489
490static void register_bridging_functions() __attribute__((constructor));
491static void register_bridging_functions()
492{
493 ipmi_register_callback(
494 NETFUN_APP, Bridging::IpmiAppBridgingCmds::ipmiCmdClearMessageFlags,
495 NULL, ipmiAppClearMessageFlags, PRIVILEGE_USER);
496
497 ipmi_register_callback(
498 NETFUN_APP, Bridging::IpmiAppBridgingCmds::ipmiCmdGetMessageFlags, NULL,
499 ipmiAppGetMessageFlags, PRIVILEGE_USER);
500
501 ipmi_register_callback(NETFUN_APP,
502 Bridging::IpmiAppBridgingCmds::ipmiCmdGetMessage,
503 NULL, ipmiAppGetMessage, PRIVILEGE_USER);
504
505 ipmi_register_callback(NETFUN_APP,
506 Bridging::IpmiAppBridgingCmds::ipmiCmdSendMessage,
507 NULL, ipmiAppSendMessage, PRIVILEGE_USER);
508
509 return;
510}