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