1bf58cd64SPatrick Venture /* 2bf58cd64SPatrick Venture * Copyright 2018 Google Inc. 3bf58cd64SPatrick Venture * 4bf58cd64SPatrick Venture * Licensed under the Apache License, Version 2.0 (the "License"); 5bf58cd64SPatrick Venture * you may not use this file except in compliance with the License. 6bf58cd64SPatrick Venture * You may obtain a copy of the License at 7bf58cd64SPatrick Venture * 8bf58cd64SPatrick Venture * http://www.apache.org/licenses/LICENSE-2.0 9bf58cd64SPatrick Venture * 10bf58cd64SPatrick Venture * Unless required by applicable law or agreed to in writing, software 11bf58cd64SPatrick Venture * distributed under the License is distributed on an "AS IS" BASIS, 12bf58cd64SPatrick Venture * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13bf58cd64SPatrick Venture * See the License for the specific language governing permissions and 14bf58cd64SPatrick Venture * limitations under the License. 15bf58cd64SPatrick Venture */ 16bf58cd64SPatrick Venture 17bf58cd64SPatrick Venture #include "updater.hpp" 18bf58cd64SPatrick Venture 19d61b0ff8SPatrick Venture #include "firmware_handler.hpp" 203ecb3503SPatrick Venture #include "status.hpp" 212bc23fe1SPatrick Venture #include "tool_errors.hpp" 227dad86fdSPatrick Venture #include "util.hpp" 230533d0b0SPatrick Venture 2400887597SPatrick Venture #include <algorithm> 25664c5bc7SPatrick Venture #include <blobs-ipmid/blobs.hpp> 26339dece8SPatrick Venture #include <cstring> 27664c5bc7SPatrick Venture #include <ipmiblob/blob_errors.hpp> 28af69625fSPatrick Venture #include <memory> 292a927e87SPatrick Venture #include <string> 30d61b0ff8SPatrick Venture #include <thread> 3155646decSPatrick Venture #include <vector> 32af69625fSPatrick Venture 339b534f06SPatrick Venture namespace host_tool 349b534f06SPatrick Venture { 359b534f06SPatrick Venture 3655646decSPatrick Venture bool UpdateHandler::checkAvailable(const std::string& goalFirmware) 3755646decSPatrick Venture { 3855646decSPatrick Venture std::vector<std::string> blobs = blob->getBlobList(); 3955646decSPatrick Venture 4055646decSPatrick Venture auto blobInst = std::find_if( 4155646decSPatrick Venture blobs.begin(), blobs.end(), [&goalFirmware](const std::string& iter) { 4255646decSPatrick Venture /* Running into weird scenarios where the string comparison doesn't 4355646decSPatrick Venture * work. TODO: revisit. 4455646decSPatrick Venture */ 4555646decSPatrick Venture return (0 == std::memcmp(goalFirmware.c_str(), iter.c_str(), 4655646decSPatrick Venture goalFirmware.length())); 4755646decSPatrick Venture // return (goalFirmware.compare(iter)); 4855646decSPatrick Venture }); 4955646decSPatrick Venture if (blobInst == blobs.end()) 5055646decSPatrick Venture { 5155646decSPatrick Venture std::fprintf(stderr, "%s not found\n", goalFirmware.c_str()); 5255646decSPatrick Venture return false; 5355646decSPatrick Venture } 5455646decSPatrick Venture 5555646decSPatrick Venture /* Call stat on /flash/image (or /flash/tarball) and check if data interface 5655646decSPatrick Venture * is supported. 5755646decSPatrick Venture */ 5855646decSPatrick Venture ipmiblob::StatResponse stat; 5955646decSPatrick Venture 6055646decSPatrick Venture try 6155646decSPatrick Venture { 6255646decSPatrick Venture stat = blob->getStat(goalFirmware); 6355646decSPatrick Venture } 6455646decSPatrick Venture catch (const ipmiblob::BlobException& b) 6555646decSPatrick Venture { 6655646decSPatrick Venture std::fprintf(stderr, "Received exception '%s' on getStat\n", b.what()); 6755646decSPatrick Venture return false; 6855646decSPatrick Venture } 6955646decSPatrick Venture 7055646decSPatrick Venture auto supported = handler->supportedType(); 7155646decSPatrick Venture if ((stat.blob_state & supported) == 0) 7255646decSPatrick Venture { 7355646decSPatrick Venture std::fprintf(stderr, "data interface selected not supported.\n"); 7455646decSPatrick Venture return false; 7555646decSPatrick Venture } 7655646decSPatrick Venture 7755646decSPatrick Venture return true; 7855646decSPatrick Venture } 7955646decSPatrick Venture 8055646decSPatrick Venture void UpdateHandler::sendFile(const std::string& target, const std::string& path) 8155646decSPatrick Venture { 8255646decSPatrick Venture std::uint16_t session; 8355646decSPatrick Venture auto supported = handler->supportedType(); 8455646decSPatrick Venture 8555646decSPatrick Venture try 8655646decSPatrick Venture { 8755646decSPatrick Venture session = blob->openBlob( 8855646decSPatrick Venture target, static_cast<std::uint16_t>(supported) | 8955646decSPatrick Venture static_cast<std::uint16_t>(blobs::OpenFlags::write)); 9055646decSPatrick Venture } 9155646decSPatrick Venture catch (const ipmiblob::BlobException& b) 9255646decSPatrick Venture { 9355646decSPatrick Venture throw ToolException("blob exception received: " + 9455646decSPatrick Venture std::string(b.what())); 9555646decSPatrick Venture } 9655646decSPatrick Venture 9755646decSPatrick Venture if (!handler->sendContents(path, session)) 9855646decSPatrick Venture { 9955646decSPatrick Venture /* Need to close the session on failure, or it's stuck open (until the 10055646decSPatrick Venture * blob handler timeout is implemented, and even then, why make it wait. 10155646decSPatrick Venture */ 10255646decSPatrick Venture blob->closeBlob(session); 10355646decSPatrick Venture throw ToolException("Failed to send contents of " + path); 10455646decSPatrick Venture } 10555646decSPatrick Venture 10655646decSPatrick Venture blob->closeBlob(session); 10755646decSPatrick Venture } 10855646decSPatrick Venture 109d61b0ff8SPatrick Venture /* Poll an open verification session. Handling closing the session is not yet 110*1f09d414SPatrick Venture * owned by this method. 111*1f09d414SPatrick Venture */ 11214713becSPatrick Venture bool pollStatus(std::uint16_t session, ipmiblob::BlobInterface* blob) 113d61b0ff8SPatrick Venture { 114d61b0ff8SPatrick Venture using namespace std::chrono_literals; 115d61b0ff8SPatrick Venture 116d61b0ff8SPatrick Venture static constexpr auto verificationSleep = 5s; 117da66fd84SPatrick Venture ipmi_flash::ActionStatus result = ipmi_flash::ActionStatus::unknown; 118d61b0ff8SPatrick Venture 119d61b0ff8SPatrick Venture try 120d61b0ff8SPatrick Venture { 121c674474cSPatrick Venture static constexpr int commandAttempts = 20; 122c674474cSPatrick Venture int attempts = 0; 123c674474cSPatrick Venture bool exitLoop = false; 124c674474cSPatrick Venture 125d61b0ff8SPatrick Venture /* Reach back the current status from the verification service output. 126d61b0ff8SPatrick Venture */ 127d61b0ff8SPatrick Venture while (attempts++ < commandAttempts) 128d61b0ff8SPatrick Venture { 129d61b0ff8SPatrick Venture ipmiblob::StatResponse resp = blob->getStat(session); 130d61b0ff8SPatrick Venture 131d61b0ff8SPatrick Venture if (resp.metadata.size() != sizeof(std::uint8_t)) 132d61b0ff8SPatrick Venture { 133d61b0ff8SPatrick Venture /* TODO: How do we want to handle the verification failures, 134d61b0ff8SPatrick Venture * because closing the session to the verify blob has a special 135d61b0ff8SPatrick Venture * as-of-yet not fully defined behavior. 136d61b0ff8SPatrick Venture */ 137d61b0ff8SPatrick Venture std::fprintf(stderr, "Received invalid metadata response!!!\n"); 138d61b0ff8SPatrick Venture } 139d61b0ff8SPatrick Venture 140da66fd84SPatrick Venture result = static_cast<ipmi_flash::ActionStatus>(resp.metadata[0]); 141d61b0ff8SPatrick Venture 142d61b0ff8SPatrick Venture switch (result) 143d61b0ff8SPatrick Venture { 144da66fd84SPatrick Venture case ipmi_flash::ActionStatus::failed: 145d61b0ff8SPatrick Venture std::fprintf(stderr, "failed\n"); 146d61b0ff8SPatrick Venture exitLoop = true; 147d61b0ff8SPatrick Venture break; 148da66fd84SPatrick Venture case ipmi_flash::ActionStatus::unknown: 149d61b0ff8SPatrick Venture std::fprintf(stderr, "other\n"); 150d61b0ff8SPatrick Venture break; 151da66fd84SPatrick Venture case ipmi_flash::ActionStatus::running: 152d61b0ff8SPatrick Venture std::fprintf(stderr, "running\n"); 153d61b0ff8SPatrick Venture break; 154da66fd84SPatrick Venture case ipmi_flash::ActionStatus::success: 155d61b0ff8SPatrick Venture std::fprintf(stderr, "success\n"); 156d61b0ff8SPatrick Venture exitLoop = true; 157d61b0ff8SPatrick Venture break; 158d61b0ff8SPatrick Venture default: 159d61b0ff8SPatrick Venture std::fprintf(stderr, "wat\n"); 160d61b0ff8SPatrick Venture } 161d61b0ff8SPatrick Venture 162d61b0ff8SPatrick Venture if (exitLoop) 163d61b0ff8SPatrick Venture { 164d61b0ff8SPatrick Venture break; 165d61b0ff8SPatrick Venture } 166d61b0ff8SPatrick Venture std::this_thread::sleep_for(verificationSleep); 167d61b0ff8SPatrick Venture } 168d61b0ff8SPatrick Venture } 169d61b0ff8SPatrick Venture catch (const ipmiblob::BlobException& b) 170d61b0ff8SPatrick Venture { 171d61b0ff8SPatrick Venture throw ToolException("blob exception received: " + 172d61b0ff8SPatrick Venture std::string(b.what())); 173d61b0ff8SPatrick Venture } 174d61b0ff8SPatrick Venture 175d61b0ff8SPatrick Venture /* TODO: If this is reached and it's not success, it may be worth just 176d61b0ff8SPatrick Venture * throwing a ToolException with a timeout message specifying the final 177d61b0ff8SPatrick Venture * read's value. 178d61b0ff8SPatrick Venture * 179d61b0ff8SPatrick Venture * TODO: Given that excepting from certain points leaves the BMC update 180d61b0ff8SPatrick Venture * state machine in an inconsistent state, we need to carefully evaluate 181d61b0ff8SPatrick Venture * which exceptions from the lower layers allow one to try and delete the 182d61b0ff8SPatrick Venture * blobs to rollback the state and progress. 183d61b0ff8SPatrick Venture */ 184da66fd84SPatrick Venture return (result == ipmi_flash::ActionStatus::success); 185d61b0ff8SPatrick Venture } 186d61b0ff8SPatrick Venture 18755646decSPatrick Venture bool UpdateHandler::verifyFile(const std::string& target) 188bf58cd64SPatrick Venture { 1890533d0b0SPatrick Venture std::uint16_t session; 19055646decSPatrick Venture bool success = false; 19155646decSPatrick Venture 1920533d0b0SPatrick Venture try 1930533d0b0SPatrick Venture { 194664c5bc7SPatrick Venture session = blob->openBlob( 19555646decSPatrick Venture target, static_cast<std::uint16_t>(blobs::OpenFlags::write)); 1967dcca5ddSPatrick Venture } 1977dcca5ddSPatrick Venture catch (const ipmiblob::BlobException& b) 1987dcca5ddSPatrick Venture { 1997dcca5ddSPatrick Venture throw ToolException("blob exception received: " + 2007dcca5ddSPatrick Venture std::string(b.what())); 2017dcca5ddSPatrick Venture } 2027dcca5ddSPatrick Venture 203f6ea2e83SPatrick Venture std::fprintf(stderr, "Committing to %s to trigger service\n", 204f6ea2e83SPatrick Venture target.c_str()); 20555646decSPatrick Venture 2067dcca5ddSPatrick Venture try 2077dcca5ddSPatrick Venture { 2087dcca5ddSPatrick Venture blob->commit(session, {}); 2097dcca5ddSPatrick Venture } 2107dcca5ddSPatrick Venture catch (const ipmiblob::BlobException& b) 2117dcca5ddSPatrick Venture { 2127dcca5ddSPatrick Venture throw ToolException("blob exception received: " + 2137dcca5ddSPatrick Venture std::string(b.what())); 2147dcca5ddSPatrick Venture } 2157dcca5ddSPatrick Venture 216f6ea2e83SPatrick Venture std::fprintf(stderr, "Calling stat on %s session to check status\n", 217f6ea2e83SPatrick Venture target.c_str()); 2187dcca5ddSPatrick Venture 21914713becSPatrick Venture if (pollStatus(session, blob)) 220d61b0ff8SPatrick Venture { 221f6ea2e83SPatrick Venture std::fprintf(stderr, "Returned success\n"); 22255646decSPatrick Venture success = true; 223d61b0ff8SPatrick Venture } 224d61b0ff8SPatrick Venture else 225d61b0ff8SPatrick Venture { 226f6ea2e83SPatrick Venture std::fprintf(stderr, "Returned non-success (could still " 227d61b0ff8SPatrick Venture "be running (unlikely))\n"); 228d61b0ff8SPatrick Venture } 229d61b0ff8SPatrick Venture 2307dcca5ddSPatrick Venture blob->closeBlob(session); 23155646decSPatrick Venture return (success == true); 23255646decSPatrick Venture } 23355646decSPatrick Venture 234*1f09d414SPatrick Venture void updaterMain(UpdateHandlerInterface* updater, const std::string& imagePath, 23555646decSPatrick Venture const std::string& signaturePath) 23655646decSPatrick Venture { 23755646decSPatrick Venture /* TODO(venture): Add optional parameter to specify the flash type, default 23855646decSPatrick Venture * to legacy for now. 23955646decSPatrick Venture */ 2401d5a31c9SPatrick Venture bool goalSupported = 2411d5a31c9SPatrick Venture updater->checkAvailable(ipmi_flash::staticLayoutBlobId); 24255646decSPatrick Venture if (!goalSupported) 24355646decSPatrick Venture { 24455646decSPatrick Venture throw ToolException("Goal firmware or interface not supported"); 24555646decSPatrick Venture } 24655646decSPatrick Venture 24755646decSPatrick Venture /* Yay, our data handler is supported. */ 24855646decSPatrick Venture 24955646decSPatrick Venture /* Send over the firmware image. */ 25055646decSPatrick Venture std::fprintf(stderr, "Sending over the firmware image.\n"); 2511d5a31c9SPatrick Venture updater->sendFile(ipmi_flash::staticLayoutBlobId, imagePath); 25255646decSPatrick Venture 25355646decSPatrick Venture /* Send over the hash contents. */ 25455646decSPatrick Venture std::fprintf(stderr, "Sending over the hash file.\n"); 2551d5a31c9SPatrick Venture updater->sendFile(ipmi_flash::hashBlobId, signaturePath); 25655646decSPatrick Venture 25714713becSPatrick Venture /* Trigger the verification by opening and committing the verify file. */ 25855646decSPatrick Venture std::fprintf(stderr, "Opening the verification file\n"); 2591d5a31c9SPatrick Venture if (updater->verifyFile(ipmi_flash::verifyBlobId)) 26055646decSPatrick Venture { 26155646decSPatrick Venture std::fprintf(stderr, "succeeded\n"); 26255646decSPatrick Venture } 26355646decSPatrick Venture else 26455646decSPatrick Venture { 26555646decSPatrick Venture std::fprintf(stderr, "failed\n"); 26614713becSPatrick Venture throw ToolException("Verification failed"); 26714713becSPatrick Venture } 26814713becSPatrick Venture 26914713becSPatrick Venture /* Trigger the update by opening and committing the update file. */ 27014713becSPatrick Venture std::fprintf(stderr, "Opening the update file\n"); 27114713becSPatrick Venture if (updater->verifyFile(ipmi_flash::updateBlobId)) 27214713becSPatrick Venture { 27314713becSPatrick Venture std::fprintf(stderr, "succeeded\n"); 27414713becSPatrick Venture } 27514713becSPatrick Venture else 27614713becSPatrick Venture { 27714713becSPatrick Venture /* Depending on the update mechanism used, this may be uninteresting. 27814713becSPatrick Venture * For instance, for the static layout, we use the reboot update 27914713becSPatrick Venture * mechanism. Which doesn't always lead to a successful return before 28014713becSPatrick Venture * the BMC starts shutting down services. 28114713becSPatrick Venture */ 28214713becSPatrick Venture std::fprintf(stderr, "failed\n"); 28314713becSPatrick Venture throw ToolException("Update failed"); 28455646decSPatrick Venture } 285bf58cd64SPatrick Venture } 2869b534f06SPatrick Venture 2879b534f06SPatrick Venture } // namespace host_tool 288