1 /* 2 * Copyright 2018 Google Inc. 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 "firmware_handler.hpp" 18 19 #include "data.hpp" 20 #include "flags.hpp" 21 #include "image_handler.hpp" 22 #include "status.hpp" 23 #include "util.hpp" 24 25 #include <blobs-ipmid/blobs.hpp> 26 27 #include <algorithm> 28 #include <cstdint> 29 #include <cstring> 30 #include <fstream> 31 #include <memory> 32 #include <string> 33 #include <vector> 34 35 namespace ipmi_flash 36 { 37 38 std::unique_ptr<blobs::GenericBlobInterface> 39 FirmwareBlobHandler::CreateFirmwareBlobHandler( 40 std::vector<HandlerPack>&& firmwares, 41 std::vector<DataHandlerPack>&& transports, ActionMap&& actionPacks) 42 { 43 /* There must be at least one in addition to the hash blob handler. */ 44 if (firmwares.size() < 2) 45 { 46 std::fprintf(stderr, "Must provide at least two firmware handlers."); 47 return nullptr; 48 } 49 if (transports.empty()) 50 { 51 return nullptr; 52 } 53 if (actionPacks.empty()) 54 { 55 return nullptr; 56 } 57 58 std::vector<std::string> blobs; 59 for (const auto& item : firmwares) 60 { 61 blobs.push_back(item.blobName); 62 } 63 64 if (0 == std::count(blobs.begin(), blobs.end(), hashBlobId)) 65 { 66 return nullptr; 67 } 68 69 return std::make_unique<FirmwareBlobHandler>(std::move(firmwares), blobs, 70 std::move(transports), 71 std::move(actionPacks)); 72 } 73 74 /* Check if the path is in our supported list (or active list). */ 75 bool FirmwareBlobHandler::canHandleBlob(const std::string& path) 76 { 77 return (std::count(blobIDs.begin(), blobIDs.end(), path) > 0); 78 } 79 80 /* 81 * Grab the list of supported firmware. 82 * 83 * If there's an open firmware session, it'll already be present in the 84 * list as "/flash/active/image", and if the hash has started, 85 * "/flash/active/hash" regardless of mechanism. This is done in the open 86 * comamnd, no extra work is required here. 87 */ 88 std::vector<std::string> FirmwareBlobHandler::getBlobIds() 89 { 90 return blobIDs; 91 } 92 93 /* 94 * Per the design, this mean abort, and this will trigger whatever 95 * appropriate actions are required to abort the process. 96 */ 97 bool FirmwareBlobHandler::deleteBlob(const std::string& path) 98 { 99 switch (state) 100 { 101 case UpdateState::notYetStarted: 102 /* Trying to delete anything at this point has no effect and returns 103 * false. 104 */ 105 return false; 106 case UpdateState::verificationPending: 107 abortProcess(); 108 return true; 109 case UpdateState::updatePending: 110 abortProcess(); 111 return true; 112 default: 113 break; 114 } 115 116 return false; 117 } 118 119 /* 120 * Stat on the files will return information such as what supported 121 * transport mechanisms are available. 122 * 123 * Stat on an active file or hash will return information such as the size 124 * of the data cached, and any additional pertinent information. The 125 * blob_state on the active files will return the state of the update. 126 */ 127 bool FirmwareBlobHandler::stat(const std::string& path, blobs::BlobMeta* meta) 128 { 129 /* We know we support this path because canHandle is called ahead */ 130 if (path == verifyBlobId || path == activeImageBlobId || 131 path == activeHashBlobId || path == updateBlobId) 132 { 133 /* These blobs are placeholders that indicate things, or allow actions, 134 * but are not stat-able as-is. 135 */ 136 return false; 137 } 138 139 /* They are requesting information about the generic blob_id. */ 140 141 /* Older host tools expect the blobState to contain a bitmask of available 142 * transport backends, so report that we support all of them in order to 143 * preserve backwards compatibility. 144 */ 145 meta->blobState = transportMask; 146 meta->size = 0; 147 return true; 148 } 149 150 ActionStatus FirmwareBlobHandler::getActionStatus() 151 { 152 ActionStatus value = ActionStatus::unknown; 153 auto* pack = getActionPack(); 154 155 switch (state) 156 { 157 case UpdateState::verificationPending: 158 value = ActionStatus::unknown; 159 break; 160 case UpdateState::verificationStarted: 161 /* If we got here, there must be data AND a hash, not just a hash, 162 * therefore pack will be known. */ 163 if (!pack) 164 { 165 break; 166 } 167 value = pack->verification->status(); 168 lastVerificationStatus = value; 169 break; 170 case UpdateState::verificationCompleted: 171 value = lastVerificationStatus; 172 break; 173 case UpdateState::updatePending: 174 value = ActionStatus::unknown; 175 break; 176 case UpdateState::updateStarted: 177 if (!pack) 178 { 179 break; 180 } 181 value = pack->update->status(); 182 lastUpdateStatus = value; 183 break; 184 case UpdateState::updateCompleted: 185 value = lastUpdateStatus; 186 break; 187 default: 188 break; 189 } 190 191 return value; 192 } 193 194 /* 195 * Return stat information on an open session. It therefore must be an active 196 * handle to either the active image or active hash. 197 */ 198 bool FirmwareBlobHandler::stat(uint16_t session, blobs::BlobMeta* meta) 199 { 200 auto item = lookup.find(session); 201 if (item == lookup.end()) 202 { 203 return false; 204 } 205 206 /* The size here refers to the size of the file -- of something analagous. 207 */ 208 meta->size = (item->second->imageHandler) 209 ? item->second->imageHandler->getSize() 210 : 0; 211 212 meta->metadata.clear(); 213 214 if (item->second->activePath == verifyBlobId || 215 item->second->activePath == updateBlobId) 216 { 217 ActionStatus value = getActionStatus(); 218 219 meta->metadata.push_back(static_cast<std::uint8_t>(value)); 220 221 /* Change the firmware handler's state and the blob's stat value 222 * depending. 223 */ 224 if (value == ActionStatus::success || value == ActionStatus::failed) 225 { 226 if (item->second->activePath == verifyBlobId) 227 { 228 changeState(UpdateState::verificationCompleted); 229 } 230 else 231 { 232 /* item->second->activePath == updateBlobId */ 233 changeState(UpdateState::updateCompleted); 234 } 235 236 item->second->flags &= ~blobs::StateFlags::committing; 237 238 if (value == ActionStatus::success) 239 { 240 item->second->flags |= blobs::StateFlags::committed; 241 } 242 else 243 { 244 item->second->flags |= blobs::StateFlags::commit_error; 245 } 246 } 247 } 248 249 /* The blobState here relates to an active sesion, so we should return the 250 * flags used to open this session. 251 */ 252 meta->blobState = item->second->flags; 253 254 /* The metadata blob returned comes from the data handler... it's used for 255 * instance, in P2A bridging to get required information about the mapping, 256 * and is the "opposite" of the lpc writemeta requirement. 257 */ 258 if (item->second->dataHandler) 259 { 260 auto bytes = item->second->dataHandler->readMeta(); 261 meta->metadata.insert(meta->metadata.begin(), bytes.begin(), 262 bytes.end()); 263 } 264 265 return true; 266 } 267 268 /* 269 * If you open /flash/image or /flash/tarball, or /flash/hash it will 270 * interpret the open flags and perform whatever actions are required for 271 * that update process. The session returned can be used immediately for 272 * sending data down, without requiring one to open the new active file. 273 * 274 * If you open the active flash image or active hash it will let you 275 * overwrite pieces, depending on the state. 276 * 277 * Once the verification process has started the active files cannot be 278 * opened. 279 * 280 * You can only have one open session at a time. Which means, you can only 281 * have one file open at a time. Trying to open the hash blob_id while you 282 * still have the flash image blob_id open will fail. Opening the flash 283 * blob_id when it is already open will fail. 284 */ 285 bool FirmwareBlobHandler::open(uint16_t session, uint16_t flags, 286 const std::string& path) 287 { 288 /* Is there an open session already? We only allow one at a time. 289 * 290 * Further on this, if there's an active session to the hash we don't allow 291 * re-opening the image, and if we have the image open, we don't allow 292 * opening the hash. This design decision may be re-evaluated, and changed 293 * to only allow one session per object type (of the two types). But, 294 * consider if the hash is open, do we want to allow writing to the image? 295 * And why would we? But, really, the point of no-return is once the 296 * verification process has begun -- which is done via commit() on the hash 297 * blob_id, we no longer want to allow updating the contents. 298 */ 299 if (fileOpen()) 300 { 301 return false; 302 } 303 304 /* The active blobs are only meant to indicate status that something has 305 * opened the image file or the hash file. 306 */ 307 if (path == activeImageBlobId || path == activeHashBlobId) 308 { 309 /* 2a) are they opening the active image? this can only happen if they 310 * already started one (due to canHandleBlob's behavior). 311 */ 312 /* 2b) are they opening the active hash? this can only happen if they 313 * already started one (due to canHandleBlob's behavior). 314 */ 315 return false; 316 } 317 318 /* Check that they've opened for writing - read back not currently 319 * supported. 320 */ 321 if ((flags & blobs::OpenFlags::write) == 0) 322 { 323 return false; 324 } 325 326 /* Because canHandleBlob is called before open, we know that if they try to 327 * open the verifyBlobId, they're in a state where it's present. 328 */ 329 330 switch (state) 331 { 332 case UpdateState::notYetStarted: 333 /* Only hashBlobId and firmware BlobIds present. */ 334 break; 335 case UpdateState::uploadInProgress: 336 /* Unreachable code because if it's started a file is open. */ 337 break; 338 case UpdateState::verificationPending: 339 /* Handle opening the verifyBlobId --> we know the image and hash 340 * aren't open because of the fileOpen() check. They can still open 341 * other files from this state to transition back into 342 * uploadInProgress. 343 * 344 * The file must be opened for writing, but no transport mechanism 345 * specified since it's irrelevant. 346 */ 347 if (path == verifyBlobId) 348 { 349 verifyImage.flags = flags; 350 351 lookup[session] = &verifyImage; 352 353 return true; 354 } 355 break; 356 case UpdateState::verificationStarted: 357 case UpdateState::verificationCompleted: 358 /* Unreachable code because if it's started a file is open. */ 359 return false; 360 case UpdateState::updatePending: 361 { 362 /* When in this state, they can only open the updateBlobId */ 363 if (path == updateBlobId) 364 { 365 updateImage.flags = flags; 366 367 lookup[session] = &updateImage; 368 369 return true; 370 } 371 else 372 { 373 return false; 374 } 375 } 376 case UpdateState::updateStarted: 377 case UpdateState::updateCompleted: 378 /* Unreachable code because if it's started a file is open. */ 379 break; 380 default: 381 break; 382 } 383 384 /* To support multiple firmware options, we need to make sure they're 385 * opening the one they already opened during this update sequence, or it's 386 * the first time they're opening it. 387 */ 388 if (path != hashBlobId) 389 { 390 /* If they're not opening the hashBlobId they must be opening a firmware 391 * handler. 392 */ 393 if (openedFirmwareType.empty()) 394 { 395 /* First time for this sequence. */ 396 openedFirmwareType = path; 397 } 398 else 399 { 400 if (openedFirmwareType != path) 401 { 402 /* Previously, in this sequence they opened /flash/image, and 403 * now they're opening /flash/bios without finishing out 404 * /flash/image (for example). 405 */ 406 std::fprintf(stderr, "Trying to open alternate firmware while " 407 "unfinished with other firmware.\n"); 408 return false; 409 } 410 } 411 } 412 413 /* There are two abstractions at play, how you get the data and how you 414 * handle that data. such that, whether the data comes from the PCI bridge 415 * or LPC bridge is not connected to whether the data goes into a static 416 * layout flash update or a UBI tarball. 417 */ 418 419 std::uint16_t transportFlag = flags & transportMask; 420 421 /* How are they expecting to copy this data? */ 422 auto d = std::find_if(transports.begin(), transports.end(), 423 [&transportFlag](const auto& iter) { 424 return (iter.bitmask == transportFlag); 425 }); 426 if (d == transports.end()) 427 { 428 return false; 429 } 430 431 /* We found the transport handler they requested */ 432 433 /* Elsewhere I do this check by checking "if ::ipmi" because that's the 434 * only non-external data pathway -- but this is just a more generic 435 * approach to that. 436 */ 437 if (d->handler) 438 { 439 /* If the data handler open call fails, open fails. */ 440 if (!d->handler->open()) 441 { 442 return false; 443 } 444 } 445 446 /* Do we have a file handler for the type of file they're opening. 447 * Note: This should only fail if something is somehow crazy wrong. 448 * Since the canHandle() said yes, and that's tied into the list of explicit 449 * firmware handers (and file handlers, like this'll know where to write the 450 * tarball, etc). 451 */ 452 auto h = std::find_if( 453 handlers.begin(), handlers.end(), 454 [&path](const auto& iter) { return (iter.blobName == path); }); 455 if (h == handlers.end()) 456 { 457 return false; 458 } 459 460 /* Ok, so we found a handler that matched, so call open() */ 461 if (!h->handler->open(path, std::ios::out)) 462 { 463 return false; 464 } 465 466 Session* curr; 467 const char* active; 468 469 if (path == hashBlobId) 470 { 471 /* 2c) are they opening the /flash/hash ? (to start the process) */ 472 curr = &activeHash; 473 active = activeHashBlobId; 474 } 475 else 476 { 477 curr = &activeImage; 478 active = activeImageBlobId; 479 } 480 481 curr->flags = flags; 482 curr->dataHandler = d->handler.get(); 483 curr->imageHandler = h->handler.get(); 484 485 lookup[session] = curr; 486 487 addBlobId(active); 488 removeBlobId(verifyBlobId); 489 490 changeState(UpdateState::uploadInProgress); 491 492 return true; 493 } 494 495 /** 496 * The write command really just grabs the data from wherever it is and sends it 497 * to the image handler. It's the image handler's responsibility to deal with 498 * the data provided. 499 * 500 * This receives a session from the blob manager, therefore it is always called 501 * between open() and close(). 502 */ 503 bool FirmwareBlobHandler::write(uint16_t session, uint32_t offset, 504 const std::vector<uint8_t>& data) 505 { 506 auto item = lookup.find(session); 507 if (item == lookup.end()) 508 { 509 return false; 510 } 511 512 /* Prevent writing during verification. */ 513 if (state == UpdateState::verificationStarted) 514 { 515 return false; 516 } 517 518 /* Prevent writing to the verification or update blobs. */ 519 if (item->second->activePath == verifyBlobId || 520 item->second->activePath == updateBlobId) 521 { 522 return false; 523 } 524 525 std::vector<std::uint8_t> bytes; 526 527 if (item->second->flags & FirmwareFlags::UpdateFlags::ipmi) 528 { 529 bytes = data; 530 } 531 else 532 { 533 /* little endian required per design, and so on, but TODO: do endianness 534 * with boost. 535 */ 536 struct ExtChunkHdr header; 537 538 if (data.size() != sizeof(header)) 539 { 540 return false; 541 } 542 543 std::memcpy(&header, data.data(), data.size()); 544 bytes = item->second->dataHandler->copyFrom(header.length); 545 } 546 547 return item->second->imageHandler->write(offset, bytes); 548 } 549 550 /* 551 * If the active session (image or hash) is over LPC, this allows 552 * configuring it. This option is only available before you start 553 * writing data for the given item (image or hash). This will return 554 * false at any other part. -- the lpc handler portion will know to return 555 * false. 556 */ 557 bool FirmwareBlobHandler::writeMeta(uint16_t session, uint32_t offset, 558 const std::vector<uint8_t>& data) 559 { 560 auto item = lookup.find(session); 561 if (item == lookup.end()) 562 { 563 return false; 564 } 565 566 if (item->second->flags & FirmwareFlags::UpdateFlags::ipmi) 567 { 568 return false; 569 } 570 571 /* Prevent writing meta to the verification blob (it has no data handler). 572 */ 573 if (item->second->dataHandler) 574 { 575 return item->second->dataHandler->writeMeta(data); 576 } 577 578 return false; 579 } 580 581 /* 582 * If this command is called on the session for the verifyBlobId, it'll 583 * trigger a systemd service `verify_image.service` to attempt to verify 584 * the image. 585 * 586 * For this file to have opened, the other two must be closed, which means any 587 * out-of-band transport mechanism involved is closed. 588 */ 589 bool FirmwareBlobHandler::commit(uint16_t session, 590 const std::vector<uint8_t>& data) 591 { 592 auto item = lookup.find(session); 593 if (item == lookup.end()) 594 { 595 return false; 596 } 597 598 /* You can only commit on the verifyBlodId or updateBlobId */ 599 if (item->second->activePath != verifyBlobId && 600 item->second->activePath != updateBlobId) 601 { 602 std::fprintf(stderr, "path: '%s' not expected for commit\n", 603 item->second->activePath.c_str()); 604 return false; 605 } 606 607 switch (state) 608 { 609 case UpdateState::verificationPending: 610 /* Set state to committing. */ 611 item->second->flags |= blobs::StateFlags::committing; 612 return triggerVerification(); 613 case UpdateState::verificationStarted: 614 /* Calling repeatedly has no effect within an update process. */ 615 return true; 616 case UpdateState::verificationCompleted: 617 /* Calling after the verification process has completed returns 618 * failure. */ 619 return false; 620 case UpdateState::updatePending: 621 item->second->flags |= blobs::StateFlags::committing; 622 return triggerUpdate(); 623 case UpdateState::updateStarted: 624 /* Calling repeatedly has no effect within an update process. */ 625 return true; 626 default: 627 return false; 628 } 629 } 630 631 /* 632 * Close must be called on the firmware image before triggering 633 * verification via commit. Once the verification is complete, you can 634 * then close the hash file. 635 * 636 * If the `verify_image.service` returned success, closing the hash file 637 * will have a specific behavior depending on the update. If it's UBI, 638 * it'll perform the install. If it's static layout, it'll do nothing. The 639 * verify_image service in the static layout case is responsible for placing 640 * the file in the correct staging position. 641 */ 642 bool FirmwareBlobHandler::close(uint16_t session) 643 { 644 auto item = lookup.find(session); 645 if (item == lookup.end()) 646 { 647 return false; 648 } 649 650 switch (state) 651 { 652 case UpdateState::uploadInProgress: 653 /* They are closing a data pathway (image, tarball, hash). */ 654 changeState(UpdateState::verificationPending); 655 656 /* Add verify blob ID now that we can expect it, IIF they also wrote 657 * some data. 658 */ 659 if (std::count(blobIDs.begin(), blobIDs.end(), activeImageBlobId)) 660 { 661 addBlobId(verifyBlobId); 662 } 663 break; 664 case UpdateState::verificationPending: 665 /* They haven't triggered, therefore closing is uninteresting. 666 */ 667 break; 668 case UpdateState::verificationStarted: 669 /* Abort without checking to see if it happened to finish. Require 670 * the caller to stat() deliberately. 671 */ 672 abortVerification(); 673 abortProcess(); 674 break; 675 case UpdateState::verificationCompleted: 676 if (lastVerificationStatus == ActionStatus::success) 677 { 678 changeState(UpdateState::updatePending); 679 addBlobId(updateBlobId); 680 removeBlobId(verifyBlobId); 681 } 682 else 683 { 684 /* Verification failed, and the host-tool knows this by calling 685 * stat(), which triggered the state change to 686 * verificationCompleted. 687 * 688 * Therefore, let's abort the process at this point. 689 */ 690 abortProcess(); 691 } 692 break; 693 case UpdateState::updatePending: 694 /* They haven't triggered the update, therefore this is 695 * uninteresting. */ 696 break; 697 case UpdateState::updateStarted: 698 /* Abort without checking to see if it happened to finish. Require 699 * the caller to stat() deliberately. 700 */ 701 abortUpdate(); 702 abortProcess(); 703 break; 704 case UpdateState::updateCompleted: 705 if (lastUpdateStatus == ActionStatus::failed) 706 { 707 /* TODO: lOG something? */ 708 std::fprintf(stderr, "Update failed\n"); 709 } 710 711 abortProcess(); 712 break; 713 default: 714 break; 715 } 716 717 if (!lookup.empty()) 718 { 719 if (item->second->dataHandler) 720 { 721 item->second->dataHandler->close(); 722 } 723 if (item->second->imageHandler) 724 { 725 item->second->imageHandler->close(); 726 } 727 lookup.erase(item); 728 } 729 return true; 730 } 731 732 void FirmwareBlobHandler::changeState(UpdateState next) 733 { 734 state = next; 735 736 if (state == UpdateState::notYetStarted) 737 { 738 /* Going back to notyetstarted, let them trigger preparation again. */ 739 preparationTriggered = false; 740 } 741 else if (state == UpdateState::uploadInProgress) 742 { 743 /* Store this transition logic here instead of ::open() */ 744 if (!preparationTriggered) 745 { 746 auto* pack = getActionPack(); 747 if (pack) 748 { 749 pack->preparation->trigger(); 750 preparationTriggered = true; 751 } 752 } 753 } 754 } 755 756 bool FirmwareBlobHandler::expire(uint16_t session) 757 { 758 abortProcess(); 759 return true; 760 } 761 762 /* 763 * Currently, the design does not provide this with a function, however, 764 * it will likely change to support reading data back. 765 */ 766 std::vector<uint8_t> FirmwareBlobHandler::read(uint16_t session, 767 uint32_t offset, 768 uint32_t requestedSize) 769 { 770 return {}; 771 } 772 773 void FirmwareBlobHandler::abortProcess() 774 { 775 /* Closing of open files is handled from close() -- Reaching here from 776 * delete may never be supported. 777 */ 778 removeBlobId(verifyBlobId); 779 removeBlobId(updateBlobId); 780 removeBlobId(activeImageBlobId); 781 removeBlobId(activeHashBlobId); 782 783 for (auto item : lookup) 784 { 785 if (item.second->dataHandler) 786 { 787 item.second->dataHandler->close(); 788 } 789 if (item.second->imageHandler) 790 { 791 item.second->imageHandler->close(); 792 } 793 } 794 lookup.clear(); 795 796 openedFirmwareType = ""; 797 changeState(UpdateState::notYetStarted); 798 } 799 800 void FirmwareBlobHandler::abortVerification() 801 { 802 auto* pack = getActionPack(); 803 if (pack) 804 { 805 pack->verification->abort(); 806 } 807 } 808 809 bool FirmwareBlobHandler::triggerVerification() 810 { 811 auto* pack = getActionPack(); 812 if (!pack) 813 { 814 return false; 815 } 816 817 bool result = pack->verification->trigger(); 818 if (result) 819 { 820 changeState(UpdateState::verificationStarted); 821 } 822 823 return result; 824 } 825 826 void FirmwareBlobHandler::abortUpdate() 827 { 828 auto* pack = getActionPack(); 829 if (pack) 830 { 831 pack->update->abort(); 832 } 833 } 834 835 bool FirmwareBlobHandler::triggerUpdate() 836 { 837 auto* pack = getActionPack(); 838 if (!pack) 839 { 840 return false; 841 } 842 843 bool result = pack->update->trigger(); 844 if (result) 845 { 846 changeState(UpdateState::updateStarted); 847 } 848 849 return result; 850 } 851 852 } // namespace ipmi_flash 853