blob: 88d29b4a47412cd10e30fe594b6aaf3adb49a260 [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
Vernon Mauerya3702c12019-05-22 13:20:59 -0700189ipmi_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 {
Vernon Mauery15419dd2019-05-24 09:40:30 -0700241 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
Vernon Mauerya3702c12019-05-22 13:20:59 -0700242 auto mesg =
Vernon Mauery15419dd2019-05-24 09:40:30 -0700243 dbus->new_method_call(ipmbBus, ipmbObj, ipmbIntf, "sendRequest");
Vernon Mauerya3702c12019-05-22 13:20:59 -0700244 ipmbRequest.prepareRequest(mesg);
Vernon Mauery15419dd2019-05-24 09:40:30 -0700245 auto ret = dbus->call(mesg);
Vernon Mauerya3702c12019-05-22 13:20:59 -0700246 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
306ipmi_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
365ipmi_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
Vernon Mauerya3702c12019-05-22 13:20:59 -0700402ipmi_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
421ipmi_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
431ipmi_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
Yong Lic3580e92019-08-15 14:36:47 +0800441std::size_t Bridging::getResponseQueueSize()
Vernon Mauerya3702c12019-05-22 13:20:59 -0700442{
Yong Lic3580e92019-08-15 14:36:47 +0800443 return responseQueue.size();
444}
Vernon Mauerya3702c12019-05-22 13:20:59 -0700445
Yong Lic3580e92019-08-15 14:36:47 +0800446/**
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**/
451ipmi::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);
Vernon Mauerya3702c12019-05-22 13:20:59 -0700485}
486
487ipmi_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
501static void register_bridging_functions() __attribute__((constructor));
502static void register_bridging_functions()
503{
504 ipmi_register_callback(
505 NETFUN_APP, Bridging::IpmiAppBridgingCmds::ipmiCmdClearMessageFlags,
506 NULL, ipmiAppClearMessageFlags, PRIVILEGE_USER);
507
Yong Lic3580e92019-08-15 14:36:47 +0800508 ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnApp,
509 ipmi::app::cmdGetMessageFlags, ipmi::Privilege::User,
510 ipmiAppGetMessageFlags);
Vernon Mauerya3702c12019-05-22 13:20:59 -0700511
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}