blob: b4f026491bc25d51e65a8a645e320f89d025e759 [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
Yong Lic3580e92019-08-15 14:36:47 +080017#include <bitset>
Vernon Mauerya3702c12019-05-22 13:20:59 -070018#include <bridgingcommands.hpp>
19#include <cstring>
Vernon Mauery15419dd2019-05-24 09:40:30 -070020#include <ipmid/api.hpp>
Yong Lic3580e92019-08-15 14:36:47 +080021#include <ipmid/utils.hpp>
Vernon Mauerya3702c12019-05-22 13:20:59 -070022#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
Yong Lic3580e92019-08-15 14:36:47 +080028static constexpr const char *wdtService = "xyz.openbmc_project.Watchdog";
29static constexpr const char *wdtInterface =
30 "xyz.openbmc_project.State.Watchdog";
31static constexpr const char *wdtObjPath = "/xyz/openbmc_project/watchdog/host0";
32static constexpr const char *wdtInterruptFlagProp =
33 "PreTimeoutInterruptOccurFlag";
34
Vernon Mauerya3702c12019-05-22 13:20:59 -070035static constexpr const char *ipmbBus = "xyz.openbmc_project.Ipmi.Channel.Ipmb";
36static constexpr const char *ipmbObj = "/xyz/openbmc_project/Ipmi/Channel/Ipmb";
37static constexpr const char *ipmbIntf = "org.openbmc.Ipmb";
38
39static Bridging bridging;
40
41/**
42 * @brief utils for checksum
43 */
44static 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
66static 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
84static inline bool ipmbConnectionHeaderChecksumValidate(ipmbHeader *ipmbHeader)
85{
86 return ipmbChecksumValidate(reinterpret_cast<uint8_t *>(ipmbHeader),
87 ipmbConnectionHeaderLength);
88}
89
90static 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
98static 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
118IpmbRequest::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
139IpmbResponse::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
155void 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
184void IpmbRequest::prepareRequest(sdbusplus::message::message &mesg)
185{
186 mesg.append(ipmbMeChannelNum, netFn, rqLun, cmd, data);
187}
188
Richard Marian Thomaiyare646a252019-11-20 22:54:03 +0530189static constexpr unsigned int makeCmdKey(unsigned int netFn, unsigned int cmd)
190{
191 return (netFn << 8) | cmd;
192}
193
194static constexpr bool isMeCmdAllowed(uint8_t netFn, uint8_t cmd)
195{
196 constexpr uint8_t netFnMeOEM = 0x2E;
197 constexpr uint8_t cmdMeOemSendRawPeci = 0x40;
198 constexpr uint8_t cmdMeOemAggSendRawPeci = 0x41;
199 constexpr uint8_t cmdMeOemCpuPkgConfWrite = 0x43;
200 constexpr uint8_t cmdMeOemCpuPciConfWrite = 0x45;
201 constexpr uint8_t cmdMeOemReadMemSmbus = 0x47;
202 constexpr uint8_t cmdMeOemWriteMemSmbus = 0x48;
203 constexpr uint8_t cmdMeOemSlotIpmb = 0x51;
204 constexpr uint8_t cmdMeOemSlotI2cMasterWriteRead = 0x52;
205 constexpr uint8_t cmdMeOemSendRawPmbus = 0xD9;
206 constexpr uint8_t cmdMeOemUnlockMeRegion = 0xE7;
207 constexpr uint8_t cmdMeOemAggSendRawPmbus = 0xEC;
208
209 switch (makeCmdKey(netFn, cmd))
210 {
211 // Restrict ME Master write command
212 case makeCmdKey(ipmi::netFnApp, ipmi::app::cmdMasterWriteRead):
213 // Restrict ME OEM commands
214 case makeCmdKey(netFnMeOEM, cmdMeOemSendRawPeci):
215 case makeCmdKey(netFnMeOEM, cmdMeOemAggSendRawPeci):
216 case makeCmdKey(netFnMeOEM, cmdMeOemCpuPkgConfWrite):
217 case makeCmdKey(netFnMeOEM, cmdMeOemCpuPciConfWrite):
218 case makeCmdKey(netFnMeOEM, cmdMeOemReadMemSmbus):
219 case makeCmdKey(netFnMeOEM, cmdMeOemWriteMemSmbus):
220 case makeCmdKey(netFnMeOEM, cmdMeOemSlotIpmb):
221 case makeCmdKey(netFnMeOEM, cmdMeOemSlotI2cMasterWriteRead):
222 case makeCmdKey(netFnMeOEM, cmdMeOemSendRawPmbus):
223 case makeCmdKey(netFnMeOEM, cmdMeOemUnlockMeRegion):
224 case makeCmdKey(netFnMeOEM, cmdMeOemAggSendRawPmbus):
225 return false;
226 default:
227 return true;
228 }
229}
230
Vernon Mauerya3702c12019-05-22 13:20:59 -0700231ipmi_return_codes Bridging::handleIpmbChannel(sSendMessageReq *sendMsgReq,
232 ipmi_response_t response,
233 ipmi_data_len_t dataLen)
234{
235 if ((*dataLen < (sizeof(sSendMessageReq) + ipmbMinFrameLength)) ||
236 (*dataLen > (sizeof(sSendMessageReq) + ipmbMaxFrameLength)))
237 {
238 *dataLen = 0;
239 return IPMI_CC_REQ_DATA_LEN_INVALID;
240 }
241
242 auto sendMsgReqData = reinterpret_cast<ipmbHeader *>(sendMsgReq->data);
243
244 // TODO: check privilege lvl. Bridging to ME requires Administrator lvl
245
246 // allow bridging to ME only
247 if (sendMsgReqData->Header.Req.address != ipmbMeSlaveAddress)
248 {
249 phosphor::logging::log<phosphor::logging::level::INFO>(
250 "handleIpmbChannel, IPMB address invalid");
251 *dataLen = 0;
252 return IPMI_CC_PARM_OUT_OF_RANGE;
253 }
254
Richard Marian Thomaiyare646a252019-11-20 22:54:03 +0530255 constexpr uint8_t shiftLUN = 2;
256 if (!isMeCmdAllowed((sendMsgReqData->Header.Req.rsNetFnLUN >> shiftLUN),
257 sendMsgReqData->Header.Req.cmd))
258 {
259 return IPMI_CC_INVALID_FIELD_REQUEST;
260 }
261
Vernon Mauerya3702c12019-05-22 13:20:59 -0700262 // check allowed modes
263 if (sendMsgReq->modeGet() != modeNoTracking &&
264 sendMsgReq->modeGet() != modeTrackRequest)
265 {
266 phosphor::logging::log<phosphor::logging::level::INFO>(
267 "handleIpmbChannel, mode not supported");
268 *dataLen = 0;
269 return IPMI_CC_PARM_OUT_OF_RANGE;
270 }
271
272 // check if request contains valid IPMB frame
273 if (!isFrameValid(sendMsgReqData, (*dataLen - sizeof(sSendMessageReq))))
274 {
275 phosphor::logging::log<phosphor::logging::level::INFO>(
276 "handleIpmbChannel, IPMB frame invalid");
277 *dataLen = 0;
278 return IPMI_CC_PARM_OUT_OF_RANGE;
279 }
280
281 auto ipmbRequest =
282 IpmbRequest(sendMsgReqData, (*dataLen - sizeof(sSendMessageReq)));
283
284 std::tuple<int, uint8_t, uint8_t, uint8_t, uint8_t, std::vector<uint8_t>>
285 ipmbResponse;
286
287 // send request to IPMB
288 try
289 {
Vernon Mauery15419dd2019-05-24 09:40:30 -0700290 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
Vernon Mauerya3702c12019-05-22 13:20:59 -0700291 auto mesg =
Vernon Mauery15419dd2019-05-24 09:40:30 -0700292 dbus->new_method_call(ipmbBus, ipmbObj, ipmbIntf, "sendRequest");
Vernon Mauerya3702c12019-05-22 13:20:59 -0700293 ipmbRequest.prepareRequest(mesg);
Vernon Mauery15419dd2019-05-24 09:40:30 -0700294 auto ret = dbus->call(mesg);
Vernon Mauerya3702c12019-05-22 13:20:59 -0700295 ret.read(ipmbResponse);
296 }
297 catch (sdbusplus::exception::SdBusError &e)
298 {
299 phosphor::logging::log<phosphor::logging::level::ERR>(
300 "handleIpmbChannel, dbus call exception");
301 *dataLen = 0;
302 return IPMI_CC_UNSPECIFIED_ERROR;
303 }
304
305 std::vector<uint8_t> dataReceived(0);
306 int status = -1;
307 uint8_t netFn = 0, lun = 0, cmd = 0, cc = 0;
308
309 std::tie(status, netFn, lun, cmd, cc, dataReceived) = ipmbResponse;
310
311 auto respReceived =
312 IpmbResponse(ipmbRequest.rqSA, netFn, lun, ipmbRequest.address,
313 ipmbRequest.seq, lun, cmd, cc, dataReceived);
314
315 // check IPMB layer status
316 if (status)
317 {
318 phosphor::logging::log<phosphor::logging::level::WARNING>(
319 "handleIpmbChannel, ipmb returned non zero status");
320 *dataLen = 0;
321 return IPMI_CC_RESPONSE_ERROR;
322 }
323
324 auto sendMsgRes = reinterpret_cast<uint8_t *>(response);
325
326 switch (sendMsgReq->modeGet())
327 {
328 case modeNoTracking:
329 if (responseQueue.size() == responseQueueMaxSize)
330 {
331 *dataLen = 0;
332 return IPMI_CC_BUSY;
333 }
334 responseQueue.insert(responseQueue.end(), std::move(respReceived));
335 *dataLen = 0;
336 return IPMI_CC_OK;
337
338 break;
339 case modeTrackRequest:
340 respReceived.ipmbToi2cConstruct(sendMsgRes, dataLen);
341 return IPMI_CC_OK;
342
343 break;
344 default:
345 phosphor::logging::log<phosphor::logging::level::INFO>(
346 "handleIpmbChannel, mode not supported");
347 *dataLen = 0;
348 return IPMI_CC_PARM_OUT_OF_RANGE;
349 }
350
351 *dataLen = 0;
352 return IPMI_CC_UNSPECIFIED_ERROR;
353}
354
355ipmi_return_codes Bridging::sendMessageHandler(ipmi_request_t request,
356 ipmi_response_t response,
357 ipmi_data_len_t dataLen)
358{
359 ipmi_return_codes retCode = IPMI_CC_OK;
360
361 if (*dataLen < sizeof(sSendMessageReq))
362 {
363 *dataLen = 0;
364 return IPMI_CC_REQ_DATA_LEN_INVALID;
365 }
366
367 auto sendMsgReq = reinterpret_cast<sSendMessageReq *>(request);
368
369 // check message fields:
370 // encryption not supported
371 if (sendMsgReq->encryptionGet() != 0)
372 {
373 phosphor::logging::log<phosphor::logging::level::INFO>(
374 "sendMessageHandler, encryption not supported");
375 *dataLen = 0;
376 return IPMI_CC_PARM_OUT_OF_RANGE;
377 }
378
379 // authentication not supported
380 if (sendMsgReq->authenticationGet() != 0)
381 {
382 phosphor::logging::log<phosphor::logging::level::INFO>(
383 "sendMessageHandler, authentication not supported");
384 *dataLen = 0;
385 return IPMI_CC_PARM_OUT_OF_RANGE;
386 }
387
388 switch (sendMsgReq->channelNumGet())
389 {
390 // we only handle ipmb for now
391 case targetChannelIpmb:
392 case targetChannelOtherLan:
393 retCode = handleIpmbChannel(sendMsgReq, response, dataLen);
394 break;
395 // fall through to default
396 case targetChannelIcmb10:
397 case targetChannelIcmb09:
398 case targetChannelLan:
399 case targetChannelSerialModem:
400 case targetChannelPciSmbus:
401 case targetChannelSmbus10:
402 case targetChannelSmbus20:
403 case targetChannelSystemInterface:
404 default:
405 phosphor::logging::log<phosphor::logging::level::INFO>(
406 "sendMessageHandler, TargetChannel invalid");
407 *dataLen = 0;
408 return IPMI_CC_PARM_OUT_OF_RANGE;
409 }
410
411 return retCode;
412}
413
414ipmi_return_codes Bridging::getMessageHandler(ipmi_request_t request,
415 ipmi_response_t response,
416 ipmi_data_len_t dataLen)
417{
418 if (*dataLen != 0)
419 {
420 *dataLen = 0;
421 return IPMI_CC_REQ_DATA_LEN_INVALID;
422 }
423
424 auto getMsgRes = reinterpret_cast<sGetMessageRes *>(response);
425 auto getMsgResData = static_cast<uint8_t *>(getMsgRes->data);
426
427 std::memset(getMsgRes, 0, sizeof(sGetMessageRes));
428
429 auto respQueueItem = responseQueue.begin();
430
431 if (respQueueItem == responseQueue.end())
432 {
433 phosphor::logging::log<phosphor::logging::level::INFO>(
434 "getMessageHandler, no data available");
435 *dataLen = 0;
436 return ipmiGetMessageCmdDataNotAvailable;
437 }
438
439 // set message fields
440 getMsgRes->privilegeLvlSet(SYSTEM_INTERFACE);
441 getMsgRes->channelNumSet(targetChannelSystemInterface);
442
443 // construct response
444 respQueueItem->ipmbToi2cConstruct(getMsgResData, dataLen);
445 responseQueue.erase(respQueueItem);
446
447 *dataLen = *dataLen + sizeof(sGetMessageRes);
448 return IPMI_CC_OK;
449}
450
Vernon Mauerya3702c12019-05-22 13:20:59 -0700451ipmi_return_codes Bridging::clearMessageFlagsHandler(ipmi_request_t request,
452 ipmi_response_t response,
453 ipmi_data_len_t dataLen)
454{
455 if (*dataLen != sizeof(sClearMessageFlagsReq))
456 {
457 return IPMI_CC_REQ_DATA_LEN_INVALID;
458 }
459
460 auto clearMsgFlagsReq = reinterpret_cast<sClearMessageFlagsReq *>(request);
461
462 if (clearMsgFlagsReq->receiveMessageBitGet() == 1)
463 {
464 responseQueue.clear();
465 }
466
467 return IPMI_CC_OK;
468}
469
470ipmi_ret_t ipmiAppSendMessage(ipmi_netfn_t netFn, ipmi_cmd_t cmd,
471 ipmi_request_t request, ipmi_response_t response,
472 ipmi_data_len_t dataLen, ipmi_context_t context)
473{
474 ipmi_ret_t retCode = IPMI_CC_OK;
475 retCode = bridging.sendMessageHandler(request, response, dataLen);
476
477 return retCode;
478}
479
480ipmi_ret_t ipmiAppGetMessage(ipmi_netfn_t netFn, ipmi_cmd_t cmd,
481 ipmi_request_t request, ipmi_response_t response,
482 ipmi_data_len_t dataLen, ipmi_context_t context)
483{
484 ipmi_ret_t retCode = IPMI_CC_OK;
485 retCode = bridging.getMessageHandler(request, response, dataLen);
486
487 return retCode;
488}
489
Yong Lic3580e92019-08-15 14:36:47 +0800490std::size_t Bridging::getResponseQueueSize()
Vernon Mauerya3702c12019-05-22 13:20:59 -0700491{
Yong Lic3580e92019-08-15 14:36:47 +0800492 return responseQueue.size();
493}
Vernon Mauerya3702c12019-05-22 13:20:59 -0700494
Yong Lic3580e92019-08-15 14:36:47 +0800495/**
496@brief This command is used to retrive present message available states.
497
498@return IPMI completion code plus Flags as response data on success.
499**/
500ipmi::RspType<std::bitset<8>> ipmiAppGetMessageFlags()
501{
502 std::bitset<8> getMsgFlagsRes;
503
504 getMsgFlagsRes.set(getMsgFlagEventMessageBit);
505
506 // set message fields
507 if (bridging.getResponseQueueSize() > 0)
508 {
509 getMsgFlagsRes.set(getMsgFlagReceiveMessageBit);
510 }
511 else
512 {
513 getMsgFlagsRes.reset(getMsgFlagReceiveMessageBit);
514 }
515
516 try
517 {
518 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
519 ipmi::Value variant = ipmi::getDbusProperty(
520 *dbus, wdtService, wdtObjPath, wdtInterface, wdtInterruptFlagProp);
521 if (std::get<bool>(variant))
522 {
523 getMsgFlagsRes.set(getMsgFlagWatchdogPreTimeOutBit);
524 }
525 }
526 catch (sdbusplus::exception::SdBusError &e)
527 {
528 phosphor::logging::log<phosphor::logging::level::ERR>(
529 "ipmiAppGetMessageFlags, dbus call exception");
530 return ipmi::responseUnspecifiedError();
531 }
532
533 return ipmi::responseSuccess(getMsgFlagsRes);
Vernon Mauerya3702c12019-05-22 13:20:59 -0700534}
535
536ipmi_ret_t ipmiAppClearMessageFlags(ipmi_netfn_t netFn, ipmi_cmd_t cmd,
537 ipmi_request_t request,
538 ipmi_response_t response,
539 ipmi_data_len_t dataLen,
540 ipmi_context_t context)
541{
542 ipmi_ret_t retCode = IPMI_CC_OK;
543 retCode = bridging.clearMessageFlagsHandler(request, response, dataLen);
544
545 *dataLen = 0;
546
547 return retCode;
548}
549
550static void register_bridging_functions() __attribute__((constructor));
551static void register_bridging_functions()
552{
553 ipmi_register_callback(
554 NETFUN_APP, Bridging::IpmiAppBridgingCmds::ipmiCmdClearMessageFlags,
555 NULL, ipmiAppClearMessageFlags, PRIVILEGE_USER);
556
Yong Lic3580e92019-08-15 14:36:47 +0800557 ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnApp,
558 ipmi::app::cmdGetMessageFlags, ipmi::Privilege::User,
559 ipmiAppGetMessageFlags);
Vernon Mauerya3702c12019-05-22 13:20:59 -0700560
561 ipmi_register_callback(NETFUN_APP,
562 Bridging::IpmiAppBridgingCmds::ipmiCmdGetMessage,
563 NULL, ipmiAppGetMessage, PRIVILEGE_USER);
564
565 ipmi_register_callback(NETFUN_APP,
566 Bridging::IpmiAppBridgingCmds::ipmiCmdSendMessage,
567 NULL, ipmiAppSendMessage, PRIVILEGE_USER);
568
569 return;
570}