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 blobs.reserve(firmwares.size()); 60 std::for_each(firmwares.begin(), firmwares.end(), 61 [&blobs](const auto& blob) { 62 blobs.emplace_back(blob.blobName); 63 }); 64 65 if (0 == std::count(blobs.begin(), blobs.end(), hashBlobId)) 66 { 67 return nullptr; 68 } 69 70 return std::make_unique<FirmwareBlobHandler>( 71 std::move(firmwares), blobs, std::move(transports), 72 std::move(actionPacks)); 73 } 74 75 /* Check if the path is in our supported list (or active list). */ 76 bool FirmwareBlobHandler::canHandleBlob(const std::string& path) 77 { 78 return (std::count(blobIDs.begin(), blobIDs.end(), path) > 0); 79 } 80 81 /* 82 * Grab the list of supported firmware. 83 * 84 * If there's an open firmware session, it'll already be present in the 85 * list as "/flash/active/image", and if the hash has started, 86 * "/flash/active/hash" regardless of mechanism. This is done in the open 87 * command, no extra work is required here. 88 */ 89 std::vector<std::string> FirmwareBlobHandler::getBlobIds() 90 { 91 return blobIDs; 92 } 93 94 /* 95 * Per the design, this mean abort, and this will trigger whatever 96 * appropriate actions are required to abort the process. 97 */ 98 bool FirmwareBlobHandler::deleteBlob(const std::string&) 99 { 100 switch (state) 101 { 102 case UpdateState::notYetStarted: 103 /* Trying to delete anything at this point has no effect and returns 104 * false. 105 */ 106 return false; 107 case UpdateState::verificationPending: 108 abortProcess(); 109 return true; 110 case UpdateState::updatePending: 111 abortProcess(); 112 return true; 113 default: 114 break; 115 } 116 117 return false; 118 } 119 120 /* 121 * Stat on the files will return information such as what supported 122 * transport mechanisms are available. 123 * 124 * Stat on an active file or hash will return information such as the size 125 * of the data cached, and any additional pertinent information. The 126 * blob_state on the active files will return the state of the update. 127 */ 128 bool FirmwareBlobHandler::stat(const std::string& path, blobs::BlobMeta* meta) 129 { 130 /* We know we support this path because canHandle is called ahead */ 131 if (path == verifyBlobId || path == activeImageBlobId || 132 path == activeHashBlobId || path == updateBlobId) 133 { 134 /* These blobs are placeholders that indicate things, or allow actions, 135 * but are not stat-able as-is. 136 */ 137 return false; 138 } 139 140 /* They are requesting information about the generic blob_id. */ 141 142 /* Older host tools expect the blobState to contain a bitmask of available 143 * transport backends, so report that we support all of them in order to 144 * preserve backwards compatibility. 145 */ 146 meta->blobState = transportMask; 147 meta->size = 0; 148 return true; 149 } 150 151 ActionStatus FirmwareBlobHandler::getActionStatus() 152 { 153 ActionStatus value = ActionStatus::unknown; 154 auto* pack = getActionPack(); 155 156 switch (state) 157 { 158 case UpdateState::verificationPending: 159 value = ActionStatus::unknown; 160 break; 161 case UpdateState::verificationStarted: 162 /* If we got here, there must be data AND a hash, not just a hash, 163 * therefore pack will be known. */ 164 if (!pack) 165 { 166 break; 167 } 168 value = pack->verification->status(); 169 lastVerificationStatus = value; 170 break; 171 case UpdateState::verificationCompleted: 172 value = lastVerificationStatus; 173 break; 174 case UpdateState::updatePending: 175 value = ActionStatus::unknown; 176 break; 177 case UpdateState::updateStarted: 178 if (!pack) 179 { 180 break; 181 } 182 value = pack->update->status(); 183 lastUpdateStatus = value; 184 break; 185 case UpdateState::updateCompleted: 186 value = lastUpdateStatus; 187 break; 188 default: 189 break; 190 } 191 192 return value; 193 } 194 195 /* 196 * Return stat information on an open session. It therefore must be an active 197 * handle to either the active image or active hash. 198 */ 199 bool FirmwareBlobHandler::stat(uint16_t session, blobs::BlobMeta* meta) 200 { 201 auto item = lookup.find(session); 202 if (item == lookup.end()) 203 { 204 return false; 205 } 206 207 /* The size here refers to the size of the file -- of something analogous. 208 */ 209 meta->size = (item->second->imageHandler) 210 ? item->second->imageHandler->getSize() 211 : 0; 212 213 meta->metadata.clear(); 214 215 if (item->second->activePath == verifyBlobId || 216 item->second->activePath == updateBlobId) 217 { 218 ActionStatus value = getActionStatus(); 219 220 meta->metadata.push_back(static_cast<std::uint8_t>(value)); 221 222 /* Change the firmware handler's state and the blob's stat value 223 * depending. 224 */ 225 if (value == ActionStatus::success || value == ActionStatus::failed) 226 { 227 if (item->second->activePath == verifyBlobId) 228 { 229 changeState(UpdateState::verificationCompleted); 230 } 231 else 232 { 233 /* item->second->activePath == updateBlobId */ 234 changeState(UpdateState::updateCompleted); 235 } 236 237 item->second->flags &= ~blobs::StateFlags::committing; 238 239 if (value == ActionStatus::success) 240 { 241 item->second->flags |= blobs::StateFlags::committed; 242 } 243 else 244 { 245 item->second->flags |= blobs::StateFlags::commit_error; 246 } 247 } 248 } 249 250 /* The blobState here relates to an active session, so we should return the 251 * flags used to open this session. 252 */ 253 meta->blobState = item->second->flags; 254 255 /* The metadata blob returned comes from the data handler... it's used for 256 * instance, in P2A bridging to get required information about the mapping, 257 * and is the "opposite" of the lpc writemeta requirement. 258 */ 259 if (item->second->dataHandler) 260 { 261 auto bytes = item->second->dataHandler->readMeta(); 262 meta->metadata.insert(meta->metadata.begin(), bytes.begin(), 263 bytes.end()); 264 } 265 266 return true; 267 } 268 269 /* 270 * If you open /flash/image or /flash/tarball, or /flash/hash it will 271 * interpret the open flags and perform whatever actions are required for 272 * that update process. The session returned can be used immediately for 273 * sending data down, without requiring one to open the new active file. 274 * 275 * If you open the active flash image or active hash it will let you 276 * overwrite pieces, depending on the state. 277 * 278 * Once the verification process has started the active files cannot be 279 * opened. 280 * 281 * You can only have one open session at a time. Which means, you can only 282 * have one file open at a time. Trying to open the hash blob_id while you 283 * still have the flash image blob_id open will fail. Opening the flash 284 * blob_id when it is already open will fail. 285 */ 286 bool FirmwareBlobHandler::open(uint16_t session, uint16_t flags, 287 const std::string& path) 288 { 289 /* Is there an open session already? We only allow one at a time. 290 * 291 * Further on this, if there's an active session to the hash we don't allow 292 * re-opening the image, and if we have the image open, we don't allow 293 * opening the hash. This design decision may be re-evaluated, and changed 294 * to only allow one session per object type (of the two types). But, 295 * consider if the hash is open, do we want to allow writing to the image? 296 * And why would we? But, really, the point of no-return is once the 297 * verification process has begun -- which is done via commit() on the hash 298 * blob_id, we no longer want to allow updating the contents. 299 */ 300 if (fileOpen()) 301 { 302 return false; 303 } 304 305 /* The active blobs are only meant to indicate status that something has 306 * opened the image file or the hash file. 307 */ 308 if (path == activeImageBlobId || path == activeHashBlobId) 309 { 310 /* 2a) are they opening the active image? this can only happen if they 311 * already started one (due to canHandleBlob's behavior). 312 */ 313 /* 2b) are they opening the active hash? this can only happen if they 314 * already started one (due to canHandleBlob's behavior). 315 */ 316 return false; 317 } 318 319 /* Check that they've opened for writing - read back not currently 320 * supported. 321 */ 322 if ((flags & blobs::OpenFlags::write) == 0) 323 { 324 return false; 325 } 326 327 /* Because canHandleBlob is called before open, we know that if they try to 328 * open the verifyBlobId, they're in a state where it's present. 329 */ 330 331 switch (state) 332 { 333 case UpdateState::notYetStarted: 334 /* Only hashBlobId and firmware BlobIds present. */ 335 break; 336 case UpdateState::uploadInProgress: 337 /* Unreachable code because if it's started a file is open. */ 338 break; 339 case UpdateState::verificationPending: 340 /* Handle opening the verifyBlobId --> we know the image and hash 341 * aren't open because of the fileOpen() check. They can still open 342 * other files from this state to transition back into 343 * uploadInProgress. 344 * 345 * The file must be opened for writing, but no transport mechanism 346 * specified since it's irrelevant. 347 */ 348 if (path == verifyBlobId) 349 { 350 verifyImage.flags = flags; 351 352 lookup[session] = &verifyImage; 353 354 return true; 355 } 356 break; 357 case UpdateState::verificationStarted: 358 case UpdateState::verificationCompleted: 359 /* Unreachable code because if it's started a file is open. */ 360 return false; 361 case UpdateState::updatePending: 362 { 363 /* When in this state, they can only open the updateBlobId */ 364 if (path == updateBlobId) 365 { 366 updateImage.flags = flags; 367 368 lookup[session] = &updateImage; 369 370 return true; 371 } 372 else 373 { 374 return false; 375 } 376 } 377 case UpdateState::updateStarted: 378 case UpdateState::updateCompleted: 379 /* Unreachable code because if it's started a file is open. */ 380 break; 381 default: 382 break; 383 } 384 385 /* To support multiple firmware options, we need to make sure they're 386 * opening the one they already opened during this update sequence, or it's 387 * the first time they're opening it. 388 */ 389 if (path != hashBlobId) 390 { 391 /* If they're not opening the hashBlobId they must be opening a firmware 392 * handler. 393 */ 394 if (openedFirmwareType.empty()) 395 { 396 /* First time for this sequence. */ 397 openedFirmwareType = path; 398 } 399 else 400 { 401 if (openedFirmwareType != path) 402 { 403 /* Previously, in this sequence they opened /flash/image, and 404 * now they're opening /flash/bios without finishing out 405 * /flash/image (for example). 406 */ 407 std::fprintf(stderr, "Trying to open alternate firmware while " 408 "unfinished with other firmware.\n"); 409 return false; 410 } 411 } 412 } 413 414 /* There are two abstractions at play, how you get the data and how you 415 * handle that data. such that, whether the data comes from the PCI bridge 416 * or LPC bridge is not connected to whether the data goes into a static 417 * layout flash update or a UBI tarball. 418 */ 419 420 std::uint16_t transportFlag = flags & transportMask; 421 422 /* How are they expecting to copy this data? */ 423 auto d = std::find_if(transports.begin(), transports.end(), 424 [&transportFlag](const auto& iter) { 425 return (iter.bitmask == transportFlag); 426 }); 427 if (d == transports.end()) 428 { 429 return false; 430 } 431 432 /* We found the transport handler they requested */ 433 434 /* Elsewhere I do this check by checking "if ::ipmi" because that's the 435 * only non-external data pathway -- but this is just a more generic 436 * approach to that. 437 */ 438 if (d->handler) 439 { 440 /* If the data handler open call fails, open fails. */ 441 if (!d->handler->open()) 442 { 443 return false; 444 } 445 } 446 447 /* Do we have a file handler for the type of file they're opening. 448 * Note: This should only fail if something is somehow crazy wrong. 449 * Since the canHandle() said yes, and that's tied into the list of explicit 450 * firmware handers (and file handlers, like this'll know where to write the 451 * tarball, etc). 452 */ 453 auto h = std::find_if( 454 handlers.begin(), handlers.end(), 455 [&path](const auto& iter) { return (iter.blobName == path); }); 456 if (h == handlers.end()) 457 { 458 return false; 459 } 460 461 /* Ok, so we found a handler that matched, so call open() */ 462 if (!h->handler->open(path, std::ios::out)) 463 { 464 return false; 465 } 466 467 Session* curr; 468 const char* active; 469 470 if (path == hashBlobId) 471 { 472 /* 2c) are they opening the /flash/hash ? (to start the process) */ 473 curr = &activeHash; 474 active = activeHashBlobId; 475 } 476 else 477 { 478 curr = &activeImage; 479 active = activeImageBlobId; 480 } 481 482 curr->flags = flags; 483 curr->dataHandler = d->handler.get(); 484 curr->imageHandler = h->handler.get(); 485 486 lookup[session] = curr; 487 488 addBlobId(active); 489 removeBlobId(verifyBlobId); 490 491 changeState(UpdateState::uploadInProgress); 492 493 return true; 494 } 495 496 /** 497 * The write command really just grabs the data from wherever it is and sends it 498 * to the image handler. It's the image handler's responsibility to deal with 499 * the data provided. 500 * 501 * This receives a session from the blob manager, therefore it is always called 502 * between open() and close(). 503 */ 504 bool FirmwareBlobHandler::write(uint16_t session, uint32_t offset, 505 const std::vector<uint8_t>& data) 506 { 507 auto item = lookup.find(session); 508 if (item == lookup.end()) 509 { 510 return false; 511 } 512 513 /* Prevent writing during verification. */ 514 if (state == UpdateState::verificationStarted) 515 { 516 return false; 517 } 518 519 /* Prevent writing to the verification or update blobs. */ 520 if (item->second->activePath == verifyBlobId || 521 item->second->activePath == updateBlobId) 522 { 523 return false; 524 } 525 526 std::vector<std::uint8_t> bytes; 527 528 if (item->second->flags & FirmwareFlags::UpdateFlags::ipmi) 529 { 530 bytes = data; 531 } 532 else 533 { 534 /* little endian required per design, and so on, but TODO: do endianness 535 * with boost. 536 */ 537 struct ExtChunkHdr header; 538 539 if (data.size() != sizeof(header)) 540 { 541 return false; 542 } 543 544 std::memcpy(&header, data.data(), data.size()); 545 bytes = item->second->dataHandler->copyFrom(header.length); 546 } 547 548 return item->second->imageHandler->write(offset, bytes); 549 } 550 551 /* 552 * If the active session (image or hash) is over LPC, this allows 553 * configuring it. This option is only available before you start 554 * writing data for the given item (image or hash). This will return 555 * false at any other part. -- the lpc handler portion will know to return 556 * false. 557 */ 558 bool FirmwareBlobHandler::writeMeta(uint16_t session, uint32_t, 559 const std::vector<uint8_t>& data) 560 { 561 auto item = lookup.find(session); 562 if (item == lookup.end()) 563 { 564 return false; 565 } 566 567 if (item->second->flags & FirmwareFlags::UpdateFlags::ipmi) 568 { 569 return false; 570 } 571 572 /* Prevent writing meta to the verification blob (it has no data handler). 573 */ 574 if (item->second->dataHandler) 575 { 576 return item->second->dataHandler->writeMeta(data); 577 } 578 579 return false; 580 } 581 582 /* 583 * If this command is called on the session for the verifyBlobId, it'll 584 * trigger a systemd service `verify_image.service` to attempt to verify 585 * the image. 586 * 587 * For this file to have opened, the other two must be closed, which means any 588 * out-of-band transport mechanism involved is closed. 589 */ 590 bool FirmwareBlobHandler::commit(uint16_t session, const std::vector<uint8_t>&) 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, IF 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) 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, uint32_t, uint32_t) 767 { 768 return {}; 769 } 770 771 void FirmwareBlobHandler::abortProcess() 772 { 773 /* Closing of open files is handled from close() -- Reaching here from 774 * delete may never be supported. 775 */ 776 removeBlobId(verifyBlobId); 777 removeBlobId(updateBlobId); 778 removeBlobId(activeImageBlobId); 779 removeBlobId(activeHashBlobId); 780 781 for (auto item : lookup) 782 { 783 if (item.second->dataHandler) 784 { 785 item.second->dataHandler->close(); 786 } 787 if (item.second->imageHandler) 788 { 789 item.second->imageHandler->close(); 790 } 791 } 792 lookup.clear(); 793 794 openedFirmwareType = ""; 795 changeState(UpdateState::notYetStarted); 796 } 797 798 void FirmwareBlobHandler::abortVerification() 799 { 800 auto* pack = getActionPack(); 801 if (pack) 802 { 803 pack->verification->abort(); 804 } 805 } 806 807 bool FirmwareBlobHandler::triggerVerification() 808 { 809 auto* pack = getActionPack(); 810 if (!pack) 811 { 812 return false; 813 } 814 815 bool result = pack->verification->trigger(); 816 if (result) 817 { 818 changeState(UpdateState::verificationStarted); 819 } 820 821 return result; 822 } 823 824 void FirmwareBlobHandler::abortUpdate() 825 { 826 auto* pack = getActionPack(); 827 if (pack) 828 { 829 pack->update->abort(); 830 } 831 } 832 833 bool FirmwareBlobHandler::triggerUpdate() 834 { 835 auto* pack = getActionPack(); 836 if (!pack) 837 { 838 return false; 839 } 840 841 bool result = pack->update->trigger(); 842 if (result) 843 { 844 changeState(UpdateState::updateStarted); 845 } 846 847 return result; 848 } 849 850 } // namespace ipmi_flash 851