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 <errno.h> 18 #include <fcntl.h> 19 #include <limits.h> 20 #include <sys/stat.h> 21 #include <sys/types.h> 22 #include <unistd.h> 23 24 #include <commandutils.hpp> 25 #include <ipmid/api.hpp> 26 #include <ipmid/utils.hpp> 27 #include <oemcommands.hpp> 28 #include <phosphor-logging/log.hpp> 29 #include <sdbusplus/message/types.hpp> 30 #include <smbiosmdrv2handler.hpp> 31 #include <xyz/openbmc_project/Common/error.hpp> 32 33 #include <cstdint> 34 #include <fstream> 35 #include <string> 36 #include <vector> 37 38 std::unique_ptr<MDRV2> mdrv2 = nullptr; 39 static constexpr const uint8_t ccOemInvalidChecksum = 0x85; 40 static constexpr size_t dataInfoSize = 16; 41 static constexpr const uint8_t ccStorageLeak = 0xC4; 42 43 static void register_netfn_smbiosmdrv2_functions() __attribute__((constructor)); 44 45 int MDRV2::agentLookup(const uint16_t& agentId) 46 { 47 int agentIndex = -1; 48 49 if (lastAgentId == agentId) 50 { 51 return lastAgentIndex; 52 } 53 54 if (agentId == smbiosAgentId) 55 { 56 return firstAgentIndex; 57 } 58 59 return agentIndex; 60 } 61 62 int MDRV2::sdplusMdrv2GetProperty(const std::string& name, 63 std::variant<uint8_t>& value, 64 const std::string& service) 65 { 66 std::shared_ptr<sdbusplus::asio::connection> bus = getSdBus(); 67 sdbusplus::message::message method = 68 bus->new_method_call(service.c_str(), mdrv2Path, dbusProperties, "Get"); 69 method.append(mdrv2Interface, name); 70 71 sdbusplus::message::message reply = bus->call(method); 72 73 try 74 { 75 sdbusplus::message::message reply = bus->call(method); 76 reply.read(value); 77 } 78 catch (sdbusplus::exception_t& e) 79 { 80 phosphor::logging::log<phosphor::logging::level::ERR>( 81 "Error get property, sdbusplus call failed", 82 phosphor::logging::entry("ERROR=%s", e.what())); 83 return -1; 84 } 85 86 return 0; 87 } 88 89 int MDRV2::syncDirCommonData(uint8_t idIndex, uint32_t size, 90 const std::string& service) 91 { 92 std::vector<uint32_t> commonData; 93 std::shared_ptr<sdbusplus::asio::connection> bus = getSdBus(); 94 sdbusplus::message::message method = 95 bus->new_method_call(service.c_str(), mdrv2Path, mdrv2Interface, 96 "SynchronizeDirectoryCommonData"); 97 method.append(idIndex, size); 98 99 try 100 { 101 sdbusplus::message::message reply = bus->call(method); 102 reply.read(commonData); 103 } 104 catch (sdbusplus::exception_t& e) 105 { 106 phosphor::logging::log<phosphor::logging::level::ERR>( 107 "Error sync dir common data with service", 108 phosphor::logging::entry("ERROR=%s", e.what())); 109 return -1; 110 } 111 112 if (commonData.size() < syncDirCommonSize) 113 { 114 phosphor::logging::log<phosphor::logging::level::ERR>( 115 "Error sync dir common data - data length invalid"); 116 return -1; 117 } 118 smbiosDir.dir[idIndex].common.dataSetSize = commonData.at(0); 119 smbiosDir.dir[idIndex].common.dataVersion = commonData.at(1); 120 smbiosDir.dir[idIndex].common.timestamp = commonData.at(2); 121 122 return 0; 123 } 124 125 int MDRV2::findDataId(const uint8_t* dataInfo, const size_t& len, 126 const std::string& service) 127 { 128 int idIndex = -1; 129 130 if (dataInfo == nullptr) 131 { 132 phosphor::logging::log<phosphor::logging::level::ERR>( 133 "Error dataInfo, input is null point"); 134 return -1; 135 } 136 137 std::shared_ptr<sdbusplus::asio::connection> bus = getSdBus(); 138 sdbusplus::message::message method = bus->new_method_call( 139 service.c_str(), mdrv2Path, mdrv2Interface, "FindIdIndex"); 140 std::vector<uint8_t> info; 141 info.resize(len); 142 std::copy(dataInfo, dataInfo + len, info.data()); 143 method.append(info); 144 145 try 146 { 147 sdbusplus::message::message reply = bus->call(method); 148 reply.read(idIndex); 149 } 150 catch (sdbusplus::exception_t& e) 151 { 152 phosphor::logging::log<phosphor::logging::level::ERR>( 153 "Error find id index", 154 phosphor::logging::entry("ERROR=%s", e.what()), 155 phosphor::logging::entry("SERVICE=%s", service.c_str()), 156 phosphor::logging::entry("PATH=%s", mdrv2Path)); 157 return -1; 158 } 159 160 return idIndex; 161 } 162 163 uint16_t MDRV2::getSessionHandle(Mdr2DirStruct* dir) 164 { 165 if (dir == NULL) 166 { 167 phosphor::logging::log<phosphor::logging::level::ERR>( 168 "Empty dir point"); 169 return 0; 170 } 171 dir->sessionHandle++; 172 if (dir->sessionHandle == 0) 173 { 174 dir->sessionHandle = 1; 175 } 176 177 return dir->sessionHandle; 178 } 179 180 int MDRV2::findLockHandle(const uint16_t& lockHandle) 181 { 182 int idIndex = -1; 183 184 for (int index = 0; index < smbiosDir.dirEntries; index++) 185 { 186 if (lockHandle == smbiosDir.dir[index].lockHandle) 187 { 188 return index; 189 } 190 } 191 192 return idIndex; 193 } 194 195 bool MDRV2::smbiosIsUpdating(uint8_t index) 196 { 197 if (index >= maxDirEntries) 198 { 199 return false; 200 } 201 if (smbiosDir.dir[index].stage == MDR2SMBIOSStatusEnum::mdr2Updating) 202 { 203 return true; 204 } 205 206 return false; 207 } 208 209 uint32_t MDRV2::calcChecksum32(uint8_t* buf, uint32_t len) 210 { 211 uint32_t sum = 0; 212 213 if (buf == nullptr) 214 { 215 return invalidChecksum; 216 } 217 218 for (uint32_t index = 0; index < len; index++) 219 { 220 sum += buf[index]; 221 } 222 223 return sum; 224 } 225 226 /** @brief implements mdr2 agent status command 227 * @param agentId 228 * @param dirVersion 229 * 230 * @returns IPMI completion code plus response data 231 * - mdrVersion 232 * - agentVersion 233 * - dirVersion 234 * - dirEntries 235 * - dataRequest 236 */ 237 ipmi::RspType<uint8_t, uint8_t, uint8_t, uint8_t, uint8_t> 238 mdr2AgentStatus(uint16_t agentId, uint8_t dirVersion) 239 { 240 if (mdrv2 == nullptr) 241 { 242 mdrv2 = std::make_unique<MDRV2>(); 243 } 244 245 int agentIndex = mdrv2->agentLookup(agentId); 246 if (agentIndex == -1) 247 { 248 phosphor::logging::log<phosphor::logging::level::ERR>( 249 "Unknown agent id", phosphor::logging::entry("ID=%x", agentId)); 250 return ipmi::responseParmOutOfRange(); 251 } 252 253 constexpr uint8_t mdrVersion = mdr2Version; 254 constexpr uint8_t agentVersion = smbiosAgentVersion; 255 uint8_t dirVersionResp = mdrv2->smbiosDir.dirVersion; 256 uint8_t dirEntries = mdrv2->smbiosDir.dirEntries; 257 uint8_t dataRequest; 258 259 if (mdrv2->smbiosDir.remoteDirVersion != dirVersion) 260 { 261 mdrv2->smbiosDir.remoteDirVersion = dirVersion; 262 dataRequest = 263 static_cast<uint8_t>(DirDataRequestEnum::dirDataRequested); 264 } 265 else 266 { 267 dataRequest = 268 static_cast<uint8_t>(DirDataRequestEnum::dirDataNotRequested); 269 } 270 271 return ipmi::responseSuccess(mdrVersion, agentVersion, dirVersionResp, 272 dirEntries, dataRequest); 273 } 274 275 /** @brief implements mdr2 get directory command 276 * @param agentId 277 * @param dirIndex 278 * @returns IPMI completion code plus response data 279 * - dataOut 280 */ 281 ipmi::RspType<std::vector<uint8_t>> mdr2GetDir(uint16_t agentId, 282 uint8_t dirIndex) 283 { 284 std::shared_ptr<sdbusplus::asio::connection> bus = getSdBus(); 285 std::string service = ipmi::getService(*bus, mdrv2Interface, mdrv2Path); 286 287 if (mdrv2 == nullptr) 288 { 289 mdrv2 = std::make_unique<MDRV2>(); 290 } 291 292 int agentIndex = mdrv2->agentLookup(agentId); 293 if (agentIndex == -1) 294 { 295 phosphor::logging::log<phosphor::logging::level::ERR>( 296 "Unknown agent id", phosphor::logging::entry("ID=%x", agentId)); 297 return ipmi::responseParmOutOfRange(); 298 } 299 300 std::variant<uint8_t> value = static_cast<uint8_t>(0); 301 if (0 != mdrv2->sdplusMdrv2GetProperty("DirectoryEntries", value, service)) 302 { 303 phosphor::logging::log<phosphor::logging::level::ERR>( 304 "Error getting DirEnries"); 305 return ipmi::responseUnspecifiedError(); 306 } 307 if (dirIndex > std::get<uint8_t>(value)) 308 { 309 return ipmi::responseParmOutOfRange(); 310 } 311 312 sdbusplus::message::message method = bus->new_method_call( 313 service.c_str(), mdrv2Path, mdrv2Interface, "GetDirectoryInformation"); 314 315 method.append(dirIndex); 316 317 std::vector<uint8_t> dataOut; 318 try 319 { 320 sdbusplus::message::message reply = bus->call(method); 321 reply.read(dataOut); 322 } 323 catch (sdbusplus::exception_t& e) 324 { 325 phosphor::logging::log<phosphor::logging::level::ERR>( 326 "Error get dir", phosphor::logging::entry("ERROR=%s", e.what()), 327 phosphor::logging::entry("SERVICE=%s", service.c_str()), 328 phosphor::logging::entry("PATH=%s", mdrv2Path)); 329 return ipmi::responseResponseError(); 330 } 331 332 constexpr size_t getDirRespSize = 6; 333 if (dataOut.size() < getDirRespSize) 334 { 335 phosphor::logging::log<phosphor::logging::level::ERR>( 336 "Error get dir, response length invalid"); 337 return ipmi::responseUnspecifiedError(); 338 } 339 340 if (dataOut.size() > MAX_IPMI_BUFFER) // length + completion code should no 341 // more than MAX_IPMI_BUFFER 342 { 343 phosphor::logging::log<phosphor::logging::level::ERR>( 344 "Data length send from service is invalid"); 345 return ipmi::responseResponseError(); 346 } 347 348 return ipmi::responseSuccess(dataOut); 349 } 350 351 /** @brief implements mdr2 send directory info command 352 * @param agentId 353 * @param dirVersion 354 * @param dirIndex 355 * @param returnedEntries 356 * @param remainingEntries 357 * @param dataInfo 358 * dataInfo is 32 Bytes in size and contains below parameters 359 * - dataInfo, size, dataSetSize, dataVersion, timestamp 360 * 361 * @returns IPMI completion code plus response data 362 * - bool 363 */ 364 365 ipmi::RspType<bool> mdr2SendDir(uint16_t agentId, uint8_t dirVersion, 366 uint8_t dirIndex, uint8_t returnedEntries, 367 uint8_t remainingEntries, 368 std::vector<uint8_t> dataInfo) 369 { 370 if ((static_cast<size_t>(returnedEntries) * dataInfoSize) != 371 dataInfo.size()) 372 { 373 return ipmi::responseReqDataLenInvalid(); 374 } 375 376 std::shared_ptr<sdbusplus::asio::connection> bus = getSdBus(); 377 std::string service = ipmi::getService(*bus, mdrv2Interface, mdrv2Path); 378 379 if (mdrv2 == nullptr) 380 { 381 mdrv2 = std::make_unique<MDRV2>(); 382 } 383 384 int agentIndex = mdrv2->agentLookup(agentId); 385 if (agentIndex == -1) 386 { 387 phosphor::logging::log<phosphor::logging::level::ERR>( 388 "Unknown agent id", phosphor::logging::entry("ID=%x", agentId)); 389 return ipmi::responseParmOutOfRange(); 390 } 391 392 if ((dirIndex + returnedEntries) > maxDirEntries) 393 { 394 phosphor::logging::log<phosphor::logging::level::ERR>( 395 "Too many directory entries"); 396 return ipmi::response(ccStorageLeak); 397 } 398 399 sdbusplus::message::message method = bus->new_method_call( 400 service.c_str(), mdrv2Path, mdrv2Interface, "SendDirectoryInformation"); 401 method.append(dirVersion, dirIndex, returnedEntries, remainingEntries, 402 dataInfo); 403 404 bool terminate = false; 405 try 406 { 407 sdbusplus::message::message reply = bus->call(method); 408 reply.read(terminate); 409 } 410 catch (sdbusplus::exception_t& e) 411 { 412 phosphor::logging::log<phosphor::logging::level::ERR>( 413 "Error send dir", phosphor::logging::entry("ERROR=%s", e.what()), 414 phosphor::logging::entry("SERVICE=%s", service.c_str()), 415 phosphor::logging::entry("PATH=%s", mdrv2Path)); 416 return ipmi::responseResponseError(); 417 } 418 419 return ipmi::responseSuccess(terminate); 420 } 421 422 /** @brief implements mdr2 get data info command 423 * @param agentId 424 * @param dataInfo 425 * 426 * @returns IPMI completion code plus response data 427 * - response - mdrVersion, data info, validFlag, 428 * dataLength, dataVersion, timeStamp 429 */ 430 ipmi::RspType<std::vector<uint8_t>> 431 mdr2GetDataInfo(uint16_t agentId, std::vector<uint8_t> dataInfo) 432 { 433 constexpr size_t getDataInfoReqSize = 16; 434 435 if (dataInfo.size() < getDataInfoReqSize) 436 { 437 return ipmi::responseReqDataLenInvalid(); 438 } 439 440 std::shared_ptr<sdbusplus::asio::connection> bus = getSdBus(); 441 std::string service = ipmi::getService(*bus, mdrv2Interface, mdrv2Path); 442 443 if (mdrv2 == nullptr) 444 { 445 mdrv2 = std::make_unique<MDRV2>(); 446 } 447 448 int agentIndex = mdrv2->agentLookup(agentId); 449 if (agentIndex == -1) 450 { 451 phosphor::logging::log<phosphor::logging::level::ERR>( 452 "Unknown agent id", phosphor::logging::entry("ID=%x", agentId)); 453 return ipmi::responseParmOutOfRange(); 454 } 455 456 int idIndex = mdrv2->findDataId(dataInfo.data(), dataInfo.size(), service); 457 458 if ((idIndex < 0) || (idIndex >= maxDirEntries)) 459 { 460 phosphor::logging::log<phosphor::logging::level::ERR>( 461 "Invalid Data ID", phosphor::logging::entry("IDINDEX=%x", idIndex)); 462 return ipmi::responseParmOutOfRange(); 463 } 464 465 sdbusplus::message::message method = bus->new_method_call( 466 service.c_str(), mdrv2Path, mdrv2Interface, "GetDataInformation"); 467 468 method.append(static_cast<uint8_t>(idIndex)); 469 470 std::vector<uint8_t> res; 471 try 472 { 473 sdbusplus::message::message reply = bus->call(method); 474 reply.read(res); 475 } 476 catch (sdbusplus::exception_t& e) 477 { 478 phosphor::logging::log<phosphor::logging::level::ERR>( 479 "Error get data info", 480 phosphor::logging::entry("ERROR=%s", e.what()), 481 phosphor::logging::entry("SERVICE=%s", service.c_str()), 482 phosphor::logging::entry("PATH=%s", mdrv2Path)); 483 return ipmi::responseResponseError(); 484 } 485 486 if (res.size() != sizeof(MDRiiGetDataInfoResponse)) 487 { 488 phosphor::logging::log<phosphor::logging::level::ERR>( 489 "Get data info response length not invalid"); 490 return ipmi::responseResponseError(); 491 } 492 493 return ipmi::responseSuccess(res); 494 } 495 496 /** @brief implements mdr2 data info offer command 497 * @param agentId - Offer a agent ID to get the "Data Set ID" 498 * 499 * @returns IPMI completion code plus response data 500 * - dataOut - data Set Id 501 */ 502 ipmi::RspType<std::vector<uint8_t>> mdr2DataInfoOffer(uint16_t agentId) 503 { 504 std::shared_ptr<sdbusplus::asio::connection> bus = getSdBus(); 505 std::string service = ipmi::getService(*bus, mdrv2Interface, mdrv2Path); 506 507 if (mdrv2 == nullptr) 508 { 509 mdrv2 = std::make_unique<MDRV2>(); 510 } 511 512 int agentIndex = mdrv2->agentLookup(agentId); 513 if (agentIndex == -1) 514 { 515 phosphor::logging::log<phosphor::logging::level::ERR>( 516 "Unknown agent id", phosphor::logging::entry("ID=%x", agentId)); 517 return ipmi::responseParmOutOfRange(); 518 } 519 520 sdbusplus::message::message method = bus->new_method_call( 521 service.c_str(), mdrv2Path, mdrv2Interface, "GetDataOffer"); 522 523 std::vector<uint8_t> dataOut; 524 try 525 { 526 sdbusplus::message::message reply = bus->call(method); 527 reply.read(dataOut); 528 } 529 catch (sdbusplus::exception_t& e) 530 { 531 phosphor::logging::log<phosphor::logging::level::ERR>( 532 "Error send data info offer", 533 phosphor::logging::entry("ERROR=%s", e.what()), 534 phosphor::logging::entry("SERVICE=%s", service.c_str()), 535 phosphor::logging::entry("PATH=%s", mdrv2Path)); 536 return ipmi::responseResponseError(); 537 } 538 539 constexpr size_t respInfoSize = 16; 540 if (dataOut.size() != respInfoSize) 541 { 542 phosphor::logging::log<phosphor::logging::level::ERR>( 543 "Error send data info offer, return length invalid"); 544 return ipmi::responseUnspecifiedError(); 545 } 546 547 return ipmi::responseSuccess(dataOut); 548 } 549 550 /** @brief implements mdr2 send data info command 551 * @param agentId 552 * @param dataInfo 553 * @param validFlag 554 * @param dataLength 555 * @param dataVersion 556 * @param timeStamp 557 * 558 * @returns IPMI completion code plus response data 559 * - bool 560 */ 561 ipmi::RspType<bool> mdr2SendDataInfo(uint16_t agentId, 562 std::array<uint8_t, dataInfoSize> dataInfo, 563 uint8_t validFlag, uint32_t dataLength, 564 uint32_t dataVersion, uint32_t timeStamp) 565 { 566 if (dataLength > smbiosTableStorageSize) 567 { 568 phosphor::logging::log<phosphor::logging::level::ERR>( 569 "Requested data length is out of SMBIOS Table storage size."); 570 return ipmi::responseParmOutOfRange(); 571 } 572 573 std::shared_ptr<sdbusplus::asio::connection> bus = getSdBus(); 574 std::string service = ipmi::getService(*bus, mdrv2Interface, mdrv2Path); 575 576 if (mdrv2 == nullptr) 577 { 578 mdrv2 = std::make_unique<MDRV2>(); 579 } 580 581 int agentIndex = mdrv2->agentLookup(agentId); 582 if (agentIndex == -1) 583 { 584 phosphor::logging::log<phosphor::logging::level::ERR>( 585 "Unknown agent id", phosphor::logging::entry("ID=%x", agentId)); 586 return ipmi::responseParmOutOfRange(); 587 } 588 589 int idIndex = mdrv2->findDataId(dataInfo.data(), dataInfo.size(), service); 590 591 if ((idIndex < 0) || (idIndex >= maxDirEntries)) 592 { 593 phosphor::logging::log<phosphor::logging::level::ERR>( 594 "Invalid Data ID", phosphor::logging::entry("IDINDEX=%x", idIndex)); 595 return ipmi::responseParmOutOfRange(); 596 } 597 598 sdbusplus::message::message method = bus->new_method_call( 599 service.c_str(), mdrv2Path, mdrv2Interface, "SendDataInformation"); 600 601 method.append((uint8_t)idIndex, validFlag, dataLength, dataVersion, 602 timeStamp); 603 604 bool entryChanged = true; 605 try 606 { 607 sdbusplus::message::message reply = bus->call(method); 608 reply.read(entryChanged); 609 } 610 catch (sdbusplus::exception_t& e) 611 { 612 phosphor::logging::log<phosphor::logging::level::ERR>( 613 "Error send data info", 614 phosphor::logging::entry("ERROR=%s", e.what()), 615 phosphor::logging::entry("SERVICE=%s", service.c_str()), 616 phosphor::logging::entry("PATH=%s", mdrv2Path)); 617 return ipmi::responseResponseError(); 618 } 619 620 return ipmi::responseSuccess(entryChanged); 621 } 622 623 /** 624 @brief This command is MDR related get data block command. 625 626 @param - agentId 627 @param - lockHandle 628 @param - xferOffset 629 @param - xferLength 630 631 @return on success 632 - xferLength 633 - checksum 634 - data 635 **/ 636 ipmi::RspType<uint32_t, // xferLength 637 uint32_t, // Checksum 638 std::vector<uint8_t> // data 639 > 640 mdr2GetDataBlock(uint16_t agentId, uint16_t lockHandle, uint32_t xferOffset, 641 uint32_t xferLength) 642 { 643 if (mdrv2 == nullptr) 644 { 645 mdrv2 = std::make_unique<MDRV2>(); 646 } 647 648 int agentIndex = mdrv2->agentLookup(agentId); 649 if (agentIndex == -1) 650 { 651 phosphor::logging::log<phosphor::logging::level::ERR>( 652 "Unknown agent id", phosphor::logging::entry("ID=%x", agentId)); 653 return ipmi::responseParmOutOfRange(); 654 } 655 656 int idIndex = mdrv2->findLockHandle(lockHandle); 657 658 if ((idIndex < 0) || (idIndex >= maxDirEntries)) 659 { 660 phosphor::logging::log<phosphor::logging::level::ERR>( 661 "Invalid Data ID", phosphor::logging::entry("IDINDEX=%x", idIndex)); 662 return ipmi::responseParmOutOfRange(); 663 } 664 665 if (xferOffset >= mdrv2->smbiosDir.dir[idIndex].common.size) 666 { 667 phosphor::logging::log<phosphor::logging::level::ERR>( 668 "Offset is outside of range."); 669 return ipmi::responseParmOutOfRange(); 670 } 671 672 size_t outSize = (xferLength > mdrv2->smbiosDir.dir[idIndex].xferSize) 673 ? mdrv2->smbiosDir.dir[idIndex].xferSize 674 : xferLength; 675 if (outSize > UINT_MAX - xferOffset) 676 { 677 phosphor::logging::log<phosphor::logging::level::ERR>( 678 "Out size and offset are out of range"); 679 return ipmi::responseParmOutOfRange(); 680 } 681 if ((xferOffset + outSize) > mdrv2->smbiosDir.dir[idIndex].common.size) 682 { 683 outSize = mdrv2->smbiosDir.dir[idIndex].common.size - xferOffset; 684 } 685 686 uint32_t respXferLength = outSize; 687 688 if (respXferLength > xferLength) 689 { 690 phosphor::logging::log<phosphor::logging::level::ERR>( 691 "Get data block unexpected error."); 692 return ipmi::responseUnspecifiedError(); 693 } 694 695 if ((xferOffset + outSize) > 696 UINT_MAX - 697 reinterpret_cast<size_t>(mdrv2->smbiosDir.dir[idIndex].dataStorage)) 698 { 699 phosphor::logging::log<phosphor::logging::level::ERR>( 700 "Input data to calculate checksum is out of range"); 701 return ipmi::responseParmOutOfRange(); 702 } 703 704 uint32_t u32Checksum = mdrv2->calcChecksum32( 705 mdrv2->smbiosDir.dir[idIndex].dataStorage + xferOffset, outSize); 706 if (u32Checksum == invalidChecksum) 707 { 708 phosphor::logging::log<phosphor::logging::level::ERR>( 709 "Get data block failed - invalid checksum"); 710 return ipmi::response(ccOemInvalidChecksum); 711 } 712 std::vector<uint8_t> data(outSize); 713 714 std::copy(&mdrv2->smbiosDir.dir[idIndex].dataStorage[xferOffset], 715 &mdrv2->smbiosDir.dir[idIndex].dataStorage[xferOffset + outSize], 716 data.begin()); 717 718 return ipmi::responseSuccess(respXferLength, u32Checksum, data); 719 } 720 721 /** @brief implements mdr2 send data block command 722 * @param agentId 723 * @param lockHandle 724 * @param xferOffset 725 * @param xferLength 726 * @param checksum 727 * 728 * @returns IPMI completion code 729 */ 730 ipmi::RspType<> mdr2SendDataBlock(uint16_t agentId, uint16_t lockHandle, 731 uint32_t xferOffset, uint32_t xferLength, 732 uint32_t checksum) 733 { 734 735 if (mdrv2 == nullptr) 736 { 737 mdrv2 = std::make_unique<MDRV2>(); 738 } 739 740 int agentIndex = mdrv2->agentLookup(agentId); 741 if (agentIndex == -1) 742 { 743 phosphor::logging::log<phosphor::logging::level::ERR>( 744 "Unknown agent id", phosphor::logging::entry("ID=%x", agentId)); 745 return ipmi::responseParmOutOfRange(); 746 } 747 748 int idIndex = mdrv2->findLockHandle(lockHandle); 749 750 if ((idIndex < 0) || (idIndex >= maxDirEntries)) 751 { 752 phosphor::logging::log<phosphor::logging::level::ERR>( 753 "Invalid Data ID", phosphor::logging::entry("IDINDEX=%x", idIndex)); 754 return ipmi::responseParmOutOfRange(); 755 } 756 757 if (mdrv2->smbiosIsUpdating(idIndex)) 758 { 759 if (xferOffset > UINT_MAX - xferLength) 760 { 761 phosphor::logging::log<phosphor::logging::level::ERR>( 762 "Offset and length are out of range"); 763 return ipmi::responseParmOutOfRange(); 764 } 765 if (((xferOffset + xferLength) > 766 mdrv2->smbiosDir.dir[idIndex].maxDataSize) || 767 ((xferOffset + xferLength) > 768 mdrv2->smbiosDir.dir[idIndex].common.dataSetSize)) 769 { 770 phosphor::logging::log<phosphor::logging::level::ERR>( 771 "Send data block Invalid offset/length"); 772 return ipmi::responseReqDataLenExceeded(); 773 } 774 if (reinterpret_cast<size_t>( 775 mdrv2->smbiosDir.dir[idIndex].dataStorage) > 776 UINT_MAX - xferOffset) 777 { 778 phosphor::logging::log<phosphor::logging::level::ERR>( 779 "Offset is out of range"); 780 return ipmi::responseParmOutOfRange(); 781 } 782 uint8_t* destAddr = 783 mdrv2->smbiosDir.dir[idIndex].dataStorage + xferOffset; 784 uint8_t* sourceAddr = reinterpret_cast<uint8_t*>(mdrv2->area->vPtr); 785 uint32_t calcChecksum = mdrv2->calcChecksum32(sourceAddr, xferLength); 786 if (calcChecksum != checksum) 787 { 788 phosphor::logging::log<phosphor::logging::level::ERR>( 789 "Send data block Invalid checksum"); 790 return ipmi::response(ccOemInvalidChecksum); 791 } 792 else 793 { 794 if (reinterpret_cast<size_t>(sourceAddr) > UINT_MAX - xferLength) 795 { 796 phosphor::logging::log<phosphor::logging::level::ERR>( 797 "Length is out of range"); 798 return ipmi::responseParmOutOfRange(); 799 } 800 std::copy(sourceAddr, sourceAddr + xferLength, destAddr); 801 } 802 } 803 else 804 { 805 phosphor::logging::log<phosphor::logging::level::ERR>( 806 "Send data block failed, other data is updating"); 807 return ipmi::responseDestinationUnavailable(); 808 } 809 810 return ipmi::responseSuccess(); 811 } 812 813 bool MDRV2::storeDatatoFlash(MDRSMBIOSHeader* mdrHdr, uint8_t* data) 814 { 815 std::ofstream smbiosFile(mdrType2File, 816 std::ios_base::binary | std::ios_base::trunc); 817 if (!smbiosFile.good()) 818 { 819 phosphor::logging::log<phosphor::logging::level::ERR>( 820 "Write data from flash error - Open MDRV2 table file failure"); 821 return false; 822 } 823 824 try 825 { 826 smbiosFile.write(reinterpret_cast<char*>(mdrHdr), 827 sizeof(MDRSMBIOSHeader)); 828 smbiosFile.write(reinterpret_cast<char*>(data), mdrHdr->dataSize); 829 } 830 catch (std::ofstream::failure& e) 831 { 832 phosphor::logging::log<phosphor::logging::level::ERR>( 833 "Write data from flash error - write data error", 834 phosphor::logging::entry("ERROR=%s", e.what())); 835 return false; 836 } 837 838 return true; 839 } 840 841 void SharedMemoryArea::Initialize(uint32_t addr, uint32_t areaSize) 842 { 843 int memDriver = 0; 844 845 // open mem driver for the system memory access 846 memDriver = open("/dev/vgasharedmem", O_RDONLY); 847 if (memDriver < 0) 848 { 849 phosphor::logging::log<phosphor::logging::level::ERR>( 850 "Cannot access mem driver"); 851 throw std::system_error(EIO, std::generic_category()); 852 } 853 854 // map the system memory 855 vPtr = mmap(NULL, // where to map to: don't mind 856 areaSize, // how many bytes ? 857 PROT_READ, // want to read and write 858 MAP_SHARED, // no copy on write 859 memDriver, // handle to /dev/mem 860 (physicalAddr & pageMask)); // hopefully the Text-buffer :-) 861 862 close(memDriver); 863 if (vPtr == MAP_FAILED) 864 { 865 phosphor::logging::log<phosphor::logging::level::ERR>( 866 "Failed to map share memory"); 867 throw std::system_error(EIO, std::generic_category()); 868 } 869 size = areaSize; 870 physicalAddr = addr; 871 } 872 873 bool MDRV2::smbiosUnlock(uint8_t index) 874 { 875 bool ret; 876 switch (smbiosDir.dir[index].stage) 877 { 878 case MDR2SMBIOSStatusEnum::mdr2Updating: 879 smbiosDir.dir[index].stage = MDR2SMBIOSStatusEnum::mdr2Updated; 880 smbiosDir.dir[index].lock = MDR2DirLockEnum::mdr2DirUnlock; 881 882 timer->stop(); 883 smbiosDir.dir[index].lockHandle = 0; 884 ret = true; 885 break; 886 887 case MDR2SMBIOSStatusEnum::mdr2Updated: 888 case MDR2SMBIOSStatusEnum::mdr2Loaded: 889 smbiosDir.dir[index].lock = MDR2DirLockEnum::mdr2DirUnlock; 890 891 timer->stop(); 892 893 smbiosDir.dir[index].lockHandle = 0; 894 ret = true; 895 break; 896 897 default: 898 break; 899 } 900 901 return ret; 902 } 903 904 bool MDRV2::smbiosTryLock(uint8_t flag, uint8_t index, uint16_t* session, 905 uint16_t timeout) 906 { 907 bool ret = false; 908 uint32_t u32Status = 0; 909 910 if (timeout == 0) 911 { 912 timeout = defaultTimeout; 913 } 914 std::chrono::microseconds usec(timeout * sysClock); 915 916 switch (smbiosDir.dir[index].stage) 917 { 918 case MDR2SMBIOSStatusEnum::mdr2Updating: 919 if (smbiosDir.dir[index].lock != MDR2DirLockEnum::mdr2DirLock) 920 { 921 smbiosDir.dir[index].lock = MDR2DirLockEnum::mdr2DirLock; 922 timer->start(usec); 923 lockIndex = index; 924 925 *session = getSessionHandle(&smbiosDir); 926 smbiosDir.dir[index].lockHandle = *session; 927 ret = true; 928 } 929 break; 930 case MDR2SMBIOSStatusEnum::mdr2Init: 931 if (flag) 932 { 933 smbiosDir.dir[index].stage = MDR2SMBIOSStatusEnum::mdr2Updating; 934 smbiosDir.dir[index].lock = MDR2DirLockEnum::mdr2DirUnlock; 935 timer->start(usec); 936 lockIndex = index; 937 938 *session = getSessionHandle(&smbiosDir); 939 smbiosDir.dir[index].lockHandle = *session; 940 ret = true; 941 } 942 break; 943 944 case MDR2SMBIOSStatusEnum::mdr2Updated: 945 case MDR2SMBIOSStatusEnum::mdr2Loaded: 946 if (smbiosDir.dir[index].lock != MDR2DirLockEnum::mdr2DirLock) 947 { 948 if (flag) 949 { 950 smbiosDir.dir[index].stage = 951 MDR2SMBIOSStatusEnum::mdr2Updating; 952 smbiosDir.dir[index].lock = MDR2DirLockEnum::mdr2DirUnlock; 953 } 954 else 955 { 956 smbiosDir.dir[index].lock = MDR2DirLockEnum::mdr2DirLock; 957 } 958 959 timer->start(usec); 960 lockIndex = index; 961 962 *session = getSessionHandle(&smbiosDir); 963 smbiosDir.dir[index].lockHandle = *session; 964 ret = true; 965 } 966 break; 967 968 default: 969 break; 970 } 971 return ret; 972 } 973 974 void MDRV2::timeoutHandler() 975 { 976 smbiosUnlock(lockIndex); 977 mdrv2->area.reset(nullptr); 978 } 979 980 /** @brief implements mdr2 lock data command 981 * @param agentId 982 * @param dataInfo 983 * @param timeout 984 * 985 * @returns IPMI completion code plus response data 986 * - mdr2Version 987 * - session 988 * - dataLength 989 * - xferAddress 990 * - xferLength 991 */ 992 ipmi::RspType<uint8_t, // mdr2Version 993 uint16_t, // session 994 uint32_t, // dataLength 995 uint32_t, // xferAddress 996 uint32_t // xferLength 997 > 998 mdr2LockData(uint16_t agentId, std::array<uint8_t, dataInfoSize> dataInfo, 999 uint16_t timeout) 1000 { 1001 if (mdrv2 == nullptr) 1002 { 1003 mdrv2 = std::make_unique<MDRV2>(); 1004 } 1005 1006 int agentIndex = mdrv2->agentLookup(agentId); 1007 if (agentIndex == -1) 1008 { 1009 phosphor::logging::log<phosphor::logging::level::ERR>( 1010 "Unknown agent id", phosphor::logging::entry("ID=%x", agentId)); 1011 return ipmi::responseParmOutOfRange(); 1012 } 1013 1014 std::shared_ptr<sdbusplus::asio::connection> bus = getSdBus(); 1015 std::string service = ipmi::getService(*bus, mdrv2Interface, mdrv2Path); 1016 1017 int idIndex = mdrv2->findDataId(dataInfo.data(), dataInfo.size(), service); 1018 1019 if ((idIndex < 0) || (idIndex >= maxDirEntries)) 1020 { 1021 phosphor::logging::log<phosphor::logging::level::ERR>( 1022 "Invalid Data ID", phosphor::logging::entry("IDINDEX=%x", idIndex)); 1023 return ipmi::responseParmOutOfRange(); 1024 } 1025 1026 uint16_t session = 0; 1027 if (!mdrv2->smbiosTryLock(0, idIndex, &session, timeout)) 1028 { 1029 phosphor::logging::log<phosphor::logging::level::ERR>( 1030 "Lock Data failed - cannot lock idIndex"); 1031 return ipmi::responseCommandNotAvailable(); 1032 } 1033 1034 uint32_t dataLength = mdrv2->smbiosDir.dir[idIndex].common.size; 1035 uint32_t xferAddress = mdrv2->smbiosDir.dir[idIndex].xferBuff; 1036 uint32_t xferLength = mdrv2->smbiosDir.dir[idIndex].xferSize; 1037 1038 return ipmi::responseSuccess(mdr2Version, session, dataLength, xferAddress, 1039 xferLength); 1040 } 1041 1042 /** @brief implements mdr2 unlock data command 1043 * @param agentId 1044 * @param lockHandle 1045 * 1046 * @returns IPMI completion code 1047 */ 1048 ipmi::RspType<> mdr2UnlockData(uint16_t agentId, uint16_t lockHandle) 1049 { 1050 phosphor::logging::log<phosphor::logging::level::ERR>("unlock data"); 1051 1052 if (mdrv2 == nullptr) 1053 { 1054 mdrv2 = std::make_unique<MDRV2>(); 1055 } 1056 1057 int agentIndex = mdrv2->agentLookup(agentId); 1058 if (agentIndex == -1) 1059 { 1060 phosphor::logging::log<phosphor::logging::level::ERR>( 1061 "Unknown agent id", phosphor::logging::entry("ID=%x", agentId)); 1062 return ipmi::responseParmOutOfRange(); 1063 } 1064 1065 int idIndex = mdrv2->findLockHandle(lockHandle); 1066 1067 if ((idIndex < 0) || (idIndex >= maxDirEntries)) 1068 { 1069 phosphor::logging::log<phosphor::logging::level::ERR>( 1070 "Invalid Data ID", phosphor::logging::entry("IDINDEX=%x", idIndex)); 1071 return ipmi::responseParmOutOfRange(); 1072 } 1073 1074 if (!mdrv2->smbiosUnlock(idIndex)) 1075 { 1076 phosphor::logging::log<phosphor::logging::level::ERR>( 1077 "Unlock Data failed - cannot unlock idIndex"); 1078 return ipmi::responseCommandNotAvailable(); 1079 } 1080 1081 return ipmi::responseSuccess(); 1082 } 1083 1084 /** 1085 @brief This command is executed after POST BIOS to get the session info. 1086 1087 @param - agentId, dataInfo, dataLength, xferAddress, xferLength, timeout. 1088 1089 @return xferStartAck and session on success. 1090 **/ 1091 ipmi::RspType<uint8_t, uint16_t> 1092 cmd_mdr2_data_start(uint16_t agentId, std::array<uint8_t, 16> dataInfo, 1093 uint32_t dataLength, uint32_t xferAddress, 1094 uint32_t xferLength, uint16_t timeout) 1095 { 1096 uint16_t session = 0; 1097 1098 if (dataLength > smbiosTableStorageSize) 1099 { 1100 phosphor::logging::log<phosphor::logging::level::ERR>( 1101 "Requested data length is out of SMBIOS Table storage size."); 1102 return ipmi::responseParmOutOfRange(); 1103 } 1104 if ((xferLength + xferAddress) > mdriiSMSize) 1105 { 1106 phosphor::logging::log<phosphor::logging::level::ERR>( 1107 "Invalid data address and size"); 1108 return ipmi::responseParmOutOfRange(); 1109 } 1110 1111 std::shared_ptr<sdbusplus::asio::connection> bus = getSdBus(); 1112 std::string service = ipmi::getService(*bus, mdrv2Interface, mdrv2Path); 1113 1114 if (mdrv2 == nullptr) 1115 { 1116 mdrv2 = std::make_unique<MDRV2>(); 1117 } 1118 1119 int agentIndex = mdrv2->agentLookup(agentId); 1120 if (agentIndex == -1) 1121 { 1122 phosphor::logging::log<phosphor::logging::level::ERR>( 1123 "Unknown agent id", phosphor::logging::entry("ID=%x", agentId)); 1124 return ipmi::responseParmOutOfRange(); 1125 } 1126 1127 int idIndex = mdrv2->findDataId(dataInfo.data(), dataInfo.size(), service); 1128 1129 if ((idIndex < 0) || (idIndex >= maxDirEntries)) 1130 { 1131 phosphor::logging::log<phosphor::logging::level::ERR>( 1132 "Invalid Data ID", phosphor::logging::entry("IDINDEX=%x", idIndex)); 1133 return ipmi::responseParmOutOfRange(); 1134 } 1135 1136 if (mdrv2->smbiosTryLock(1, idIndex, &session, timeout)) 1137 { 1138 try 1139 { 1140 mdrv2->area = 1141 std::make_unique<SharedMemoryArea>(xferAddress, xferLength); 1142 } 1143 catch (const std::system_error& e) 1144 { 1145 mdrv2->smbiosUnlock(idIndex); 1146 phosphor::logging::log<phosphor::logging::level::ERR>( 1147 "Unable to access share memory", 1148 phosphor::logging::entry("ERROR=%s", e.what())); 1149 return ipmi::responseUnspecifiedError(); 1150 } 1151 mdrv2->smbiosDir.dir[idIndex].common.size = dataLength; 1152 mdrv2->smbiosDir.dir[idIndex].lockHandle = session; 1153 if (-1 == 1154 mdrv2->syncDirCommonData( 1155 idIndex, mdrv2->smbiosDir.dir[idIndex].common.size, service)) 1156 { 1157 phosphor::logging::log<phosphor::logging::level::ERR>( 1158 "Unable to sync data to service"); 1159 return ipmi::responseResponseError(); 1160 } 1161 } 1162 else 1163 { 1164 phosphor::logging::log<phosphor::logging::level::ERR>( 1165 "Canot lock smbios"); 1166 return ipmi::responseUnspecifiedError(); 1167 } 1168 1169 static constexpr uint8_t xferStartAck = 1; 1170 1171 return ipmi::responseSuccess(xferStartAck, session); 1172 } 1173 1174 /** 1175 @brief This command is executed to close the session. 1176 1177 @param - agentId, lockHandle. 1178 1179 @return completion code on success. 1180 **/ 1181 ipmi::RspType<> cmd_mdr2_data_done(uint16_t agentId, uint16_t lockHandle) 1182 { 1183 1184 if (mdrv2 == nullptr) 1185 { 1186 mdrv2 = std::make_unique<MDRV2>(); 1187 } 1188 1189 int agentIndex = mdrv2->agentLookup(agentId); 1190 if (agentIndex == -1) 1191 { 1192 phosphor::logging::log<phosphor::logging::level::ERR>( 1193 "Unknown agent id", phosphor::logging::entry("ID=%x", agentId)); 1194 return ipmi::responseParmOutOfRange(); 1195 } 1196 1197 int idIndex = mdrv2->findLockHandle(lockHandle); 1198 1199 if ((idIndex < 0) || (idIndex >= maxDirEntries)) 1200 { 1201 phosphor::logging::log<phosphor::logging::level::ERR>( 1202 "Invalid Data ID", phosphor::logging::entry("IDINDEX=%x", idIndex)); 1203 return ipmi::responseParmOutOfRange(); 1204 } 1205 1206 if (!mdrv2->smbiosUnlock(idIndex)) 1207 { 1208 phosphor::logging::log<phosphor::logging::level::ERR>( 1209 "Send data done failed - cannot unlock idIndex"); 1210 return ipmi::responseDestinationUnavailable(); 1211 } 1212 1213 mdrv2->area.reset(nullptr); 1214 MDRSMBIOSHeader mdr2Smbios; 1215 mdr2Smbios.mdrType = mdrTypeII; 1216 mdr2Smbios.dirVer = mdrv2->smbiosDir.dir[0].common.dataVersion; 1217 mdr2Smbios.timestamp = mdrv2->smbiosDir.dir[0].common.timestamp; 1218 mdr2Smbios.dataSize = mdrv2->smbiosDir.dir[0].common.size; 1219 1220 if (access(smbiosPath, 0) == -1) 1221 { 1222 int flag = mkdir(smbiosPath, S_IRWXU); 1223 if (flag != 0) 1224 { 1225 phosphor::logging::log<phosphor::logging::level::ERR>( 1226 "create folder failed for writting smbios file"); 1227 } 1228 } 1229 if (!mdrv2->storeDatatoFlash( 1230 &mdr2Smbios, mdrv2->smbiosDir.dir[smbiosDirIndex].dataStorage)) 1231 { 1232 phosphor::logging::log<phosphor::logging::level::ERR>( 1233 "MDR2 Store data to flash failed"); 1234 return ipmi::responseDestinationUnavailable(); 1235 } 1236 bool status = false; 1237 std::shared_ptr<sdbusplus::asio::connection> bus = getSdBus(); 1238 std::string service = ipmi::getService(*bus, mdrv2Interface, mdrv2Path); 1239 sdbusplus::message::message method = bus->new_method_call( 1240 service.c_str(), mdrv2Path, mdrv2Interface, "AgentSynchronizeData"); 1241 1242 try 1243 { 1244 sdbusplus::message::message reply = bus->call(method); 1245 reply.read(status); 1246 } 1247 catch (sdbusplus::exception_t& e) 1248 { 1249 phosphor::logging::log<phosphor::logging::level::ERR>( 1250 "Error Sync data with service", 1251 phosphor::logging::entry("ERROR=%s", e.what()), 1252 phosphor::logging::entry("SERVICE=%s", service.c_str()), 1253 phosphor::logging::entry("PATH=%s", mdrv2Path)); 1254 return ipmi::responseResponseError(); 1255 } 1256 1257 if (!status) 1258 { 1259 phosphor::logging::log<phosphor::logging::level::ERR>( 1260 "Sync data with service failure"); 1261 return ipmi::responseUnspecifiedError(); 1262 } 1263 1264 return ipmi::responseSuccess(); 1265 } 1266 1267 static void register_netfn_smbiosmdrv2_functions(void) 1268 { 1269 // MDR V2 Command 1270 // <Get MDRII Status Command> 1271 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnApp, 1272 ipmi::intel::app::cmdMdrIIAgentStatus, 1273 ipmi::Privilege::Operator, mdr2AgentStatus); 1274 1275 // <Get MDRII Directory Command> 1276 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnApp, 1277 ipmi::intel::app::cmdMdrIIGetDir, 1278 ipmi::Privilege::Operator, mdr2GetDir); 1279 1280 // <Send MDRII Directory Command> 1281 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnApp, 1282 ipmi::intel::app::cmdMdrIISendDir, 1283 ipmi::Privilege::Operator, mdr2SendDir); 1284 1285 // <Get MDRII Data Info Command> 1286 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnApp, 1287 ipmi::intel::app::cmdMdrIIGetDataInfo, 1288 ipmi::Privilege::Operator, mdr2GetDataInfo); 1289 1290 // <Send MDRII Info Offer> 1291 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnApp, 1292 ipmi::intel::app::cmdMdrIISendDataInfoOffer, 1293 ipmi::Privilege::Operator, mdr2DataInfoOffer); 1294 1295 // <Send MDRII Data Info> 1296 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnApp, 1297 ipmi::intel::app::cmdMdrIISendDataInfo, 1298 ipmi::Privilege::Operator, mdr2SendDataInfo); 1299 1300 // <Get MDRII Data Block Command> 1301 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnApp, 1302 ipmi::intel::app::cmdMdrIIGetDataBlock, 1303 ipmi::Privilege::Operator, mdr2GetDataBlock); 1304 1305 // <Send MDRII Data Block> 1306 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnApp, 1307 ipmi::intel::app::cmdMdrIISendDataBlock, 1308 ipmi::Privilege::Operator, mdr2SendDataBlock); 1309 1310 // <Lock MDRII Data Command> 1311 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnApp, 1312 ipmi::intel::app::cmdMdrIILockData, 1313 ipmi::Privilege::Operator, mdr2LockData); 1314 1315 // <Unlock MDRII Data Command> 1316 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnApp, 1317 ipmi::intel::app::cmdMdrIIUnlockData, 1318 ipmi::Privilege::Operator, mdr2UnlockData); 1319 1320 // <Send MDRII Data Start> 1321 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnApp, 1322 ipmi::intel::app::cmdMdrIIDataStart, 1323 ipmi::Privilege::Operator, cmd_mdr2_data_start); 1324 1325 // <Send MDRII Data Done> 1326 ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnApp, 1327 ipmi::intel::app::cmdMdrIIDataDone, 1328 ipmi::Privilege::Operator, cmd_mdr2_data_done); 1329 } 1330