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&) 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, 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, const std::vector<uint8_t>&) 590 { 591 auto item = lookup.find(session); 592 if (item == lookup.end()) 593 { 594 return false; 595 } 596 597 /* You can only commit on the verifyBlodId or updateBlobId */ 598 if (item->second->activePath != verifyBlobId && 599 item->second->activePath != updateBlobId) 600 { 601 std::fprintf(stderr, "path: '%s' not expected for commit\n", 602 item->second->activePath.c_str()); 603 return false; 604 } 605 606 switch (state) 607 { 608 case UpdateState::verificationPending: 609 /* Set state to committing. */ 610 item->second->flags |= blobs::StateFlags::committing; 611 return triggerVerification(); 612 case UpdateState::verificationStarted: 613 /* Calling repeatedly has no effect within an update process. */ 614 return true; 615 case UpdateState::verificationCompleted: 616 /* Calling after the verification process has completed returns 617 * failure. */ 618 return false; 619 case UpdateState::updatePending: 620 item->second->flags |= blobs::StateFlags::committing; 621 return triggerUpdate(); 622 case UpdateState::updateStarted: 623 /* Calling repeatedly has no effect within an update process. */ 624 return true; 625 default: 626 return false; 627 } 628 } 629 630 /* 631 * Close must be called on the firmware image before triggering 632 * verification via commit. Once the verification is complete, you can 633 * then close the hash file. 634 * 635 * If the `verify_image.service` returned success, closing the hash file 636 * will have a specific behavior depending on the update. If it's UBI, 637 * it'll perform the install. If it's static layout, it'll do nothing. The 638 * verify_image service in the static layout case is responsible for placing 639 * the file in the correct staging position. 640 */ 641 bool FirmwareBlobHandler::close(uint16_t session) 642 { 643 auto item = lookup.find(session); 644 if (item == lookup.end()) 645 { 646 return false; 647 } 648 649 switch (state) 650 { 651 case UpdateState::uploadInProgress: 652 /* They are closing a data pathway (image, tarball, hash). */ 653 changeState(UpdateState::verificationPending); 654 655 /* Add verify blob ID now that we can expect it, IIF they also wrote 656 * some data. 657 */ 658 if (std::count(blobIDs.begin(), blobIDs.end(), activeImageBlobId)) 659 { 660 addBlobId(verifyBlobId); 661 } 662 break; 663 case UpdateState::verificationPending: 664 /* They haven't triggered, therefore closing is uninteresting. 665 */ 666 break; 667 case UpdateState::verificationStarted: 668 /* Abort without checking to see if it happened to finish. Require 669 * the caller to stat() deliberately. 670 */ 671 abortVerification(); 672 abortProcess(); 673 break; 674 case UpdateState::verificationCompleted: 675 if (lastVerificationStatus == ActionStatus::success) 676 { 677 changeState(UpdateState::updatePending); 678 addBlobId(updateBlobId); 679 removeBlobId(verifyBlobId); 680 } 681 else 682 { 683 /* Verification failed, and the host-tool knows this by calling 684 * stat(), which triggered the state change to 685 * verificationCompleted. 686 * 687 * Therefore, let's abort the process at this point. 688 */ 689 abortProcess(); 690 } 691 break; 692 case UpdateState::updatePending: 693 /* They haven't triggered the update, therefore this is 694 * uninteresting. */ 695 break; 696 case UpdateState::updateStarted: 697 /* Abort without checking to see if it happened to finish. Require 698 * the caller to stat() deliberately. 699 */ 700 abortUpdate(); 701 abortProcess(); 702 break; 703 case UpdateState::updateCompleted: 704 if (lastUpdateStatus == ActionStatus::failed) 705 { 706 /* TODO: lOG something? */ 707 std::fprintf(stderr, "Update failed\n"); 708 } 709 710 abortProcess(); 711 break; 712 default: 713 break; 714 } 715 716 if (!lookup.empty()) 717 { 718 if (item->second->dataHandler) 719 { 720 item->second->dataHandler->close(); 721 } 722 if (item->second->imageHandler) 723 { 724 item->second->imageHandler->close(); 725 } 726 lookup.erase(item); 727 } 728 return true; 729 } 730 731 void FirmwareBlobHandler::changeState(UpdateState next) 732 { 733 state = next; 734 735 if (state == UpdateState::notYetStarted) 736 { 737 /* Going back to notyetstarted, let them trigger preparation again. */ 738 preparationTriggered = false; 739 } 740 else if (state == UpdateState::uploadInProgress) 741 { 742 /* Store this transition logic here instead of ::open() */ 743 if (!preparationTriggered) 744 { 745 auto* pack = getActionPack(); 746 if (pack) 747 { 748 pack->preparation->trigger(); 749 preparationTriggered = true; 750 } 751 } 752 } 753 } 754 755 bool FirmwareBlobHandler::expire(uint16_t) 756 { 757 abortProcess(); 758 return true; 759 } 760 761 /* 762 * Currently, the design does not provide this with a function, however, 763 * it will likely change to support reading data back. 764 */ 765 std::vector<uint8_t> FirmwareBlobHandler::read(uint16_t, uint32_t, uint32_t) 766 { 767 return {}; 768 } 769 770 void FirmwareBlobHandler::abortProcess() 771 { 772 /* Closing of open files is handled from close() -- Reaching here from 773 * delete may never be supported. 774 */ 775 removeBlobId(verifyBlobId); 776 removeBlobId(updateBlobId); 777 removeBlobId(activeImageBlobId); 778 removeBlobId(activeHashBlobId); 779 780 for (auto item : lookup) 781 { 782 if (item.second->dataHandler) 783 { 784 item.second->dataHandler->close(); 785 } 786 if (item.second->imageHandler) 787 { 788 item.second->imageHandler->close(); 789 } 790 } 791 lookup.clear(); 792 793 openedFirmwareType = ""; 794 changeState(UpdateState::notYetStarted); 795 } 796 797 void FirmwareBlobHandler::abortVerification() 798 { 799 auto* pack = getActionPack(); 800 if (pack) 801 { 802 pack->verification->abort(); 803 } 804 } 805 806 bool FirmwareBlobHandler::triggerVerification() 807 { 808 auto* pack = getActionPack(); 809 if (!pack) 810 { 811 return false; 812 } 813 814 bool result = pack->verification->trigger(); 815 if (result) 816 { 817 changeState(UpdateState::verificationStarted); 818 } 819 820 return result; 821 } 822 823 void FirmwareBlobHandler::abortUpdate() 824 { 825 auto* pack = getActionPack(); 826 if (pack) 827 { 828 pack->update->abort(); 829 } 830 } 831 832 bool FirmwareBlobHandler::triggerUpdate() 833 { 834 auto* pack = getActionPack(); 835 if (!pack) 836 { 837 return false; 838 } 839 840 bool result = pack->update->trigger(); 841 if (result) 842 { 843 changeState(UpdateState::updateStarted); 844 } 845 846 return result; 847 } 848 849 } // namespace ipmi_flash 850