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