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