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