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" 202bc23fe1SPatrick Venture #include "tool_errors.hpp" 21*7dad86fdSPatrick Venture #include "util.hpp" 220533d0b0SPatrick Venture 2300887597SPatrick Venture #include <algorithm> 24664c5bc7SPatrick Venture #include <blobs-ipmid/blobs.hpp> 25339dece8SPatrick Venture #include <cstring> 26664c5bc7SPatrick Venture #include <ipmiblob/blob_errors.hpp> 27af69625fSPatrick Venture #include <memory> 282a927e87SPatrick Venture #include <string> 29d61b0ff8SPatrick Venture #include <thread> 3055646decSPatrick Venture #include <vector> 31af69625fSPatrick Venture 329b534f06SPatrick Venture namespace host_tool 339b534f06SPatrick Venture { 349b534f06SPatrick Venture 3555646decSPatrick Venture bool UpdateHandler::checkAvailable(const std::string& goalFirmware) 3655646decSPatrick Venture { 3755646decSPatrick Venture std::vector<std::string> blobs = blob->getBlobList(); 3855646decSPatrick Venture 3955646decSPatrick Venture auto blobInst = std::find_if( 4055646decSPatrick Venture blobs.begin(), blobs.end(), [&goalFirmware](const std::string& iter) { 4155646decSPatrick Venture /* Running into weird scenarios where the string comparison doesn't 4255646decSPatrick Venture * work. TODO: revisit. 4355646decSPatrick Venture */ 4455646decSPatrick Venture return (0 == std::memcmp(goalFirmware.c_str(), iter.c_str(), 4555646decSPatrick Venture goalFirmware.length())); 4655646decSPatrick Venture // return (goalFirmware.compare(iter)); 4755646decSPatrick Venture }); 4855646decSPatrick Venture if (blobInst == blobs.end()) 4955646decSPatrick Venture { 5055646decSPatrick Venture std::fprintf(stderr, "%s not found\n", goalFirmware.c_str()); 5155646decSPatrick Venture return false; 5255646decSPatrick Venture } 5355646decSPatrick Venture 5455646decSPatrick Venture /* Call stat on /flash/image (or /flash/tarball) and check if data interface 5555646decSPatrick Venture * is supported. 5655646decSPatrick Venture */ 5755646decSPatrick Venture ipmiblob::StatResponse stat; 5855646decSPatrick Venture 5955646decSPatrick Venture try 6055646decSPatrick Venture { 6155646decSPatrick Venture stat = blob->getStat(goalFirmware); 6255646decSPatrick Venture } 6355646decSPatrick Venture catch (const ipmiblob::BlobException& b) 6455646decSPatrick Venture { 6555646decSPatrick Venture std::fprintf(stderr, "Received exception '%s' on getStat\n", b.what()); 6655646decSPatrick Venture return false; 6755646decSPatrick Venture } 6855646decSPatrick Venture 6955646decSPatrick Venture auto supported = handler->supportedType(); 7055646decSPatrick Venture if ((stat.blob_state & supported) == 0) 7155646decSPatrick Venture { 7255646decSPatrick Venture std::fprintf(stderr, "data interface selected not supported.\n"); 7355646decSPatrick Venture return false; 7455646decSPatrick Venture } 7555646decSPatrick Venture 7655646decSPatrick Venture return true; 7755646decSPatrick Venture } 7855646decSPatrick Venture 7955646decSPatrick Venture void UpdateHandler::sendFile(const std::string& target, const std::string& path) 8055646decSPatrick Venture { 8155646decSPatrick Venture std::uint16_t session; 8255646decSPatrick Venture auto supported = handler->supportedType(); 8355646decSPatrick Venture 8455646decSPatrick Venture try 8555646decSPatrick Venture { 8655646decSPatrick Venture session = blob->openBlob( 8755646decSPatrick Venture target, static_cast<std::uint16_t>(supported) | 8855646decSPatrick Venture static_cast<std::uint16_t>(blobs::OpenFlags::write)); 8955646decSPatrick Venture } 9055646decSPatrick Venture catch (const ipmiblob::BlobException& b) 9155646decSPatrick Venture { 9255646decSPatrick Venture throw ToolException("blob exception received: " + 9355646decSPatrick Venture std::string(b.what())); 9455646decSPatrick Venture } 9555646decSPatrick Venture 9655646decSPatrick Venture if (!handler->sendContents(path, session)) 9755646decSPatrick Venture { 9855646decSPatrick Venture /* Need to close the session on failure, or it's stuck open (until the 9955646decSPatrick Venture * blob handler timeout is implemented, and even then, why make it wait. 10055646decSPatrick Venture */ 10155646decSPatrick Venture blob->closeBlob(session); 10255646decSPatrick Venture throw ToolException("Failed to send contents of " + path); 10355646decSPatrick Venture } 10455646decSPatrick Venture 10555646decSPatrick Venture blob->closeBlob(session); 10655646decSPatrick Venture } 10755646decSPatrick Venture 108d61b0ff8SPatrick Venture /* Poll an open verification session. Handling closing the session is not yet 109d61b0ff8SPatrick Venture * owned by this method. */ 110d61b0ff8SPatrick Venture bool pollVerificationStatus(std::uint16_t session, 111d61b0ff8SPatrick Venture ipmiblob::BlobInterface* blob) 112d61b0ff8SPatrick Venture { 113d61b0ff8SPatrick Venture using namespace std::chrono_literals; 114d61b0ff8SPatrick Venture 115d61b0ff8SPatrick Venture static constexpr auto verificationSleep = 5s; 116d61b0ff8SPatrick Venture static constexpr int commandAttempts = 20; 117d61b0ff8SPatrick Venture int attempts = 0; 118d61b0ff8SPatrick Venture bool exitLoop = false; 119d61b0ff8SPatrick Venture blobs::FirmwareBlobHandler::VerifyCheckResponses result = 120d61b0ff8SPatrick Venture blobs::FirmwareBlobHandler::VerifyCheckResponses::other; 121d61b0ff8SPatrick Venture 122d61b0ff8SPatrick Venture try 123d61b0ff8SPatrick Venture { 124d61b0ff8SPatrick Venture /* Reach back the current status from the verification service output. 125d61b0ff8SPatrick Venture */ 126d61b0ff8SPatrick Venture while (attempts++ < commandAttempts) 127d61b0ff8SPatrick Venture { 128d61b0ff8SPatrick Venture ipmiblob::StatResponse resp = blob->getStat(session); 129d61b0ff8SPatrick Venture 130d61b0ff8SPatrick Venture if (resp.metadata.size() != sizeof(std::uint8_t)) 131d61b0ff8SPatrick Venture { 132d61b0ff8SPatrick Venture /* TODO: How do we want to handle the verification failures, 133d61b0ff8SPatrick Venture * because closing the session to the verify blob has a special 134d61b0ff8SPatrick Venture * as-of-yet not fully defined behavior. 135d61b0ff8SPatrick Venture */ 136d61b0ff8SPatrick Venture std::fprintf(stderr, "Received invalid metadata response!!!\n"); 137d61b0ff8SPatrick Venture } 138d61b0ff8SPatrick Venture 139d61b0ff8SPatrick Venture result = 140d61b0ff8SPatrick Venture static_cast<blobs::FirmwareBlobHandler::VerifyCheckResponses>( 141d61b0ff8SPatrick Venture resp.metadata[0]); 142d61b0ff8SPatrick Venture 143d61b0ff8SPatrick Venture switch (result) 144d61b0ff8SPatrick Venture { 145d61b0ff8SPatrick Venture case blobs::FirmwareBlobHandler::VerifyCheckResponses::failed: 146d61b0ff8SPatrick Venture std::fprintf(stderr, "failed\n"); 147d61b0ff8SPatrick Venture exitLoop = true; 148d61b0ff8SPatrick Venture break; 149d61b0ff8SPatrick Venture case blobs::FirmwareBlobHandler::VerifyCheckResponses::other: 150d61b0ff8SPatrick Venture std::fprintf(stderr, "other\n"); 151d61b0ff8SPatrick Venture break; 152d61b0ff8SPatrick Venture case blobs::FirmwareBlobHandler::VerifyCheckResponses::running: 153d61b0ff8SPatrick Venture std::fprintf(stderr, "running\n"); 154d61b0ff8SPatrick Venture break; 155d61b0ff8SPatrick Venture case blobs::FirmwareBlobHandler::VerifyCheckResponses::success: 156d61b0ff8SPatrick Venture std::fprintf(stderr, "success\n"); 157d61b0ff8SPatrick Venture exitLoop = true; 158d61b0ff8SPatrick Venture break; 159d61b0ff8SPatrick Venture default: 160d61b0ff8SPatrick Venture std::fprintf(stderr, "wat\n"); 161d61b0ff8SPatrick Venture } 162d61b0ff8SPatrick Venture 163d61b0ff8SPatrick Venture if (exitLoop) 164d61b0ff8SPatrick Venture { 165d61b0ff8SPatrick Venture break; 166d61b0ff8SPatrick Venture } 167d61b0ff8SPatrick Venture std::this_thread::sleep_for(verificationSleep); 168d61b0ff8SPatrick Venture } 169d61b0ff8SPatrick Venture } 170d61b0ff8SPatrick Venture catch (const ipmiblob::BlobException& b) 171d61b0ff8SPatrick Venture { 172d61b0ff8SPatrick Venture throw ToolException("blob exception received: " + 173d61b0ff8SPatrick Venture std::string(b.what())); 174d61b0ff8SPatrick Venture } 175d61b0ff8SPatrick Venture 176d61b0ff8SPatrick Venture /* TODO: If this is reached and it's not success, it may be worth just 177d61b0ff8SPatrick Venture * throwing a ToolException with a timeout message specifying the final 178d61b0ff8SPatrick Venture * read's value. 179d61b0ff8SPatrick Venture * 180d61b0ff8SPatrick Venture * TODO: Given that excepting from certain points leaves the BMC update 181d61b0ff8SPatrick Venture * state machine in an inconsistent state, we need to carefully evaluate 182d61b0ff8SPatrick Venture * which exceptions from the lower layers allow one to try and delete the 183d61b0ff8SPatrick Venture * blobs to rollback the state and progress. 184d61b0ff8SPatrick Venture */ 185d61b0ff8SPatrick Venture return (result == 186d61b0ff8SPatrick Venture blobs::FirmwareBlobHandler::VerifyCheckResponses::success); 187d61b0ff8SPatrick Venture } 188d61b0ff8SPatrick Venture 18955646decSPatrick Venture bool UpdateHandler::verifyFile(const std::string& target) 190bf58cd64SPatrick Venture { 1910533d0b0SPatrick Venture std::uint16_t session; 19255646decSPatrick Venture bool success = false; 19355646decSPatrick Venture 1940533d0b0SPatrick Venture try 1950533d0b0SPatrick Venture { 196664c5bc7SPatrick Venture session = blob->openBlob( 19755646decSPatrick Venture target, static_cast<std::uint16_t>(blobs::OpenFlags::write)); 1987dcca5ddSPatrick Venture } 1997dcca5ddSPatrick Venture catch (const ipmiblob::BlobException& b) 2007dcca5ddSPatrick Venture { 2017dcca5ddSPatrick Venture throw ToolException("blob exception received: " + 2027dcca5ddSPatrick Venture std::string(b.what())); 2037dcca5ddSPatrick Venture } 2047dcca5ddSPatrick Venture 2057dcca5ddSPatrick Venture std::fprintf( 2067dcca5ddSPatrick Venture stderr, 2077dcca5ddSPatrick Venture "Committing to verification file to trigger verification service\n"); 20855646decSPatrick Venture 2097dcca5ddSPatrick Venture try 2107dcca5ddSPatrick Venture { 2117dcca5ddSPatrick Venture blob->commit(session, {}); 2127dcca5ddSPatrick Venture } 2137dcca5ddSPatrick Venture catch (const ipmiblob::BlobException& b) 2147dcca5ddSPatrick Venture { 2157dcca5ddSPatrick Venture throw ToolException("blob exception received: " + 2167dcca5ddSPatrick Venture std::string(b.what())); 2177dcca5ddSPatrick Venture } 2187dcca5ddSPatrick Venture 219d61b0ff8SPatrick Venture std::fprintf(stderr, 220d61b0ff8SPatrick Venture "Calling stat on verification session to check status\n"); 2217dcca5ddSPatrick Venture 222d61b0ff8SPatrick Venture if (pollVerificationStatus(session, blob)) 223d61b0ff8SPatrick Venture { 224d61b0ff8SPatrick Venture std::fprintf(stderr, "Verification returned success\n"); 22555646decSPatrick Venture success = true; 226d61b0ff8SPatrick Venture } 227d61b0ff8SPatrick Venture else 228d61b0ff8SPatrick Venture { 229d61b0ff8SPatrick Venture std::fprintf(stderr, "Verification returned non-success (could still " 230d61b0ff8SPatrick Venture "be running (unlikely))\n"); 231d61b0ff8SPatrick Venture } 232d61b0ff8SPatrick Venture 2337dcca5ddSPatrick Venture blob->closeBlob(session); 23455646decSPatrick Venture return (success == true); 23555646decSPatrick Venture } 23655646decSPatrick Venture 23755646decSPatrick Venture void updaterMain(UpdateHandler* updater, const std::string& imagePath, 23855646decSPatrick Venture const std::string& signaturePath) 23955646decSPatrick Venture { 24055646decSPatrick Venture /* TODO(venture): Add optional parameter to specify the flash type, default 24155646decSPatrick Venture * to legacy for now. 24255646decSPatrick Venture */ 243*7dad86fdSPatrick Venture bool goalSupported = updater->checkAvailable(blobs::staticLayoutBlobId); 24455646decSPatrick Venture if (!goalSupported) 24555646decSPatrick Venture { 24655646decSPatrick Venture throw ToolException("Goal firmware or interface not supported"); 24755646decSPatrick Venture } 24855646decSPatrick Venture 24955646decSPatrick Venture /* Yay, our data handler is supported. */ 25055646decSPatrick Venture 25155646decSPatrick Venture /* Send over the firmware image. */ 25255646decSPatrick Venture std::fprintf(stderr, "Sending over the firmware image.\n"); 253*7dad86fdSPatrick Venture updater->sendFile(blobs::staticLayoutBlobId, imagePath); 25455646decSPatrick Venture 25555646decSPatrick Venture /* Send over the hash contents. */ 25655646decSPatrick Venture std::fprintf(stderr, "Sending over the hash file.\n"); 257*7dad86fdSPatrick Venture updater->sendFile(blobs::hashBlobId, signaturePath); 25855646decSPatrick Venture 25955646decSPatrick Venture /* Trigger the verification by opening the verify file. */ 26055646decSPatrick Venture std::fprintf(stderr, "Opening the verification file\n"); 261*7dad86fdSPatrick Venture if (updater->verifyFile(blobs::verifyBlobId)) 26255646decSPatrick Venture { 26355646decSPatrick Venture std::fprintf(stderr, "succeeded\n"); 26455646decSPatrick Venture } 26555646decSPatrick Venture else 26655646decSPatrick Venture { 26755646decSPatrick Venture std::fprintf(stderr, "failed\n"); 26855646decSPatrick Venture } 269bf58cd64SPatrick Venture } 2709b534f06SPatrick Venture 2719b534f06SPatrick Venture } // namespace host_tool 272