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" 210533d0b0SPatrick Venture 2200887597SPatrick Venture #include <algorithm> 23664c5bc7SPatrick Venture #include <blobs-ipmid/blobs.hpp> 24339dece8SPatrick Venture #include <cstring> 25664c5bc7SPatrick Venture #include <ipmiblob/blob_errors.hpp> 26af69625fSPatrick Venture #include <memory> 272a927e87SPatrick Venture #include <string> 28d61b0ff8SPatrick Venture #include <thread> 29*55646decSPatrick Venture #include <vector> 30af69625fSPatrick Venture 319b534f06SPatrick Venture namespace host_tool 329b534f06SPatrick Venture { 339b534f06SPatrick Venture 34*55646decSPatrick Venture bool UpdateHandler::checkAvailable(const std::string& goalFirmware) 35*55646decSPatrick Venture { 36*55646decSPatrick Venture std::vector<std::string> blobs = blob->getBlobList(); 37*55646decSPatrick Venture 38*55646decSPatrick Venture auto blobInst = std::find_if( 39*55646decSPatrick Venture blobs.begin(), blobs.end(), [&goalFirmware](const std::string& iter) { 40*55646decSPatrick Venture /* Running into weird scenarios where the string comparison doesn't 41*55646decSPatrick Venture * work. TODO: revisit. 42*55646decSPatrick Venture */ 43*55646decSPatrick Venture return (0 == std::memcmp(goalFirmware.c_str(), iter.c_str(), 44*55646decSPatrick Venture goalFirmware.length())); 45*55646decSPatrick Venture // return (goalFirmware.compare(iter)); 46*55646decSPatrick Venture }); 47*55646decSPatrick Venture if (blobInst == blobs.end()) 48*55646decSPatrick Venture { 49*55646decSPatrick Venture std::fprintf(stderr, "%s not found\n", goalFirmware.c_str()); 50*55646decSPatrick Venture return false; 51*55646decSPatrick Venture } 52*55646decSPatrick Venture 53*55646decSPatrick Venture /* Call stat on /flash/image (or /flash/tarball) and check if data interface 54*55646decSPatrick Venture * is supported. 55*55646decSPatrick Venture */ 56*55646decSPatrick Venture ipmiblob::StatResponse stat; 57*55646decSPatrick Venture 58*55646decSPatrick Venture try 59*55646decSPatrick Venture { 60*55646decSPatrick Venture stat = blob->getStat(goalFirmware); 61*55646decSPatrick Venture } 62*55646decSPatrick Venture catch (const ipmiblob::BlobException& b) 63*55646decSPatrick Venture { 64*55646decSPatrick Venture std::fprintf(stderr, "Received exception '%s' on getStat\n", b.what()); 65*55646decSPatrick Venture return false; 66*55646decSPatrick Venture } 67*55646decSPatrick Venture 68*55646decSPatrick Venture auto supported = handler->supportedType(); 69*55646decSPatrick Venture if ((stat.blob_state & supported) == 0) 70*55646decSPatrick Venture { 71*55646decSPatrick Venture std::fprintf(stderr, "data interface selected not supported.\n"); 72*55646decSPatrick Venture return false; 73*55646decSPatrick Venture } 74*55646decSPatrick Venture 75*55646decSPatrick Venture return true; 76*55646decSPatrick Venture } 77*55646decSPatrick Venture 78*55646decSPatrick Venture void UpdateHandler::sendFile(const std::string& target, const std::string& path) 79*55646decSPatrick Venture { 80*55646decSPatrick Venture std::uint16_t session; 81*55646decSPatrick Venture auto supported = handler->supportedType(); 82*55646decSPatrick Venture 83*55646decSPatrick Venture try 84*55646decSPatrick Venture { 85*55646decSPatrick Venture session = blob->openBlob( 86*55646decSPatrick Venture target, static_cast<std::uint16_t>(supported) | 87*55646decSPatrick Venture static_cast<std::uint16_t>(blobs::OpenFlags::write)); 88*55646decSPatrick Venture } 89*55646decSPatrick Venture catch (const ipmiblob::BlobException& b) 90*55646decSPatrick Venture { 91*55646decSPatrick Venture throw ToolException("blob exception received: " + 92*55646decSPatrick Venture std::string(b.what())); 93*55646decSPatrick Venture } 94*55646decSPatrick Venture 95*55646decSPatrick Venture if (!handler->sendContents(path, session)) 96*55646decSPatrick Venture { 97*55646decSPatrick Venture /* Need to close the session on failure, or it's stuck open (until the 98*55646decSPatrick Venture * blob handler timeout is implemented, and even then, why make it wait. 99*55646decSPatrick Venture */ 100*55646decSPatrick Venture blob->closeBlob(session); 101*55646decSPatrick Venture throw ToolException("Failed to send contents of " + path); 102*55646decSPatrick Venture } 103*55646decSPatrick Venture 104*55646decSPatrick Venture blob->closeBlob(session); 105*55646decSPatrick Venture } 106*55646decSPatrick Venture 107d61b0ff8SPatrick Venture /* Poll an open verification session. Handling closing the session is not yet 108d61b0ff8SPatrick Venture * owned by this method. */ 109d61b0ff8SPatrick Venture bool pollVerificationStatus(std::uint16_t session, 110d61b0ff8SPatrick Venture ipmiblob::BlobInterface* blob) 111d61b0ff8SPatrick Venture { 112d61b0ff8SPatrick Venture using namespace std::chrono_literals; 113d61b0ff8SPatrick Venture 114d61b0ff8SPatrick Venture static constexpr auto verificationSleep = 5s; 115d61b0ff8SPatrick Venture static constexpr int commandAttempts = 20; 116d61b0ff8SPatrick Venture int attempts = 0; 117d61b0ff8SPatrick Venture bool exitLoop = false; 118d61b0ff8SPatrick Venture blobs::FirmwareBlobHandler::VerifyCheckResponses result = 119d61b0ff8SPatrick Venture blobs::FirmwareBlobHandler::VerifyCheckResponses::other; 120d61b0ff8SPatrick Venture 121d61b0ff8SPatrick Venture try 122d61b0ff8SPatrick Venture { 123d61b0ff8SPatrick Venture /* Reach back the current status from the verification service output. 124d61b0ff8SPatrick Venture */ 125d61b0ff8SPatrick Venture while (attempts++ < commandAttempts) 126d61b0ff8SPatrick Venture { 127d61b0ff8SPatrick Venture ipmiblob::StatResponse resp = blob->getStat(session); 128d61b0ff8SPatrick Venture 129d61b0ff8SPatrick Venture if (resp.metadata.size() != sizeof(std::uint8_t)) 130d61b0ff8SPatrick Venture { 131d61b0ff8SPatrick Venture /* TODO: How do we want to handle the verification failures, 132d61b0ff8SPatrick Venture * because closing the session to the verify blob has a special 133d61b0ff8SPatrick Venture * as-of-yet not fully defined behavior. 134d61b0ff8SPatrick Venture */ 135d61b0ff8SPatrick Venture std::fprintf(stderr, "Received invalid metadata response!!!\n"); 136d61b0ff8SPatrick Venture } 137d61b0ff8SPatrick Venture 138d61b0ff8SPatrick Venture result = 139d61b0ff8SPatrick Venture static_cast<blobs::FirmwareBlobHandler::VerifyCheckResponses>( 140d61b0ff8SPatrick Venture resp.metadata[0]); 141d61b0ff8SPatrick Venture 142d61b0ff8SPatrick Venture switch (result) 143d61b0ff8SPatrick Venture { 144d61b0ff8SPatrick Venture case blobs::FirmwareBlobHandler::VerifyCheckResponses::failed: 145d61b0ff8SPatrick Venture std::fprintf(stderr, "failed\n"); 146d61b0ff8SPatrick Venture exitLoop = true; 147d61b0ff8SPatrick Venture break; 148d61b0ff8SPatrick Venture case blobs::FirmwareBlobHandler::VerifyCheckResponses::other: 149d61b0ff8SPatrick Venture std::fprintf(stderr, "other\n"); 150d61b0ff8SPatrick Venture break; 151d61b0ff8SPatrick Venture case blobs::FirmwareBlobHandler::VerifyCheckResponses::running: 152d61b0ff8SPatrick Venture std::fprintf(stderr, "running\n"); 153d61b0ff8SPatrick Venture break; 154d61b0ff8SPatrick Venture case blobs::FirmwareBlobHandler::VerifyCheckResponses::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 */ 184d61b0ff8SPatrick Venture return (result == 185d61b0ff8SPatrick Venture blobs::FirmwareBlobHandler::VerifyCheckResponses::success); 186d61b0ff8SPatrick Venture } 187d61b0ff8SPatrick Venture 188*55646decSPatrick Venture bool UpdateHandler::verifyFile(const std::string& target) 189bf58cd64SPatrick Venture { 1900533d0b0SPatrick Venture std::uint16_t session; 191*55646decSPatrick Venture bool success = false; 192*55646decSPatrick Venture 1930533d0b0SPatrick Venture try 1940533d0b0SPatrick Venture { 195664c5bc7SPatrick Venture session = blob->openBlob( 196*55646decSPatrick Venture target, static_cast<std::uint16_t>(blobs::OpenFlags::write)); 1977dcca5ddSPatrick Venture } 1987dcca5ddSPatrick Venture catch (const ipmiblob::BlobException& b) 1997dcca5ddSPatrick Venture { 2007dcca5ddSPatrick Venture throw ToolException("blob exception received: " + 2017dcca5ddSPatrick Venture std::string(b.what())); 2027dcca5ddSPatrick Venture } 2037dcca5ddSPatrick Venture 2047dcca5ddSPatrick Venture std::fprintf( 2057dcca5ddSPatrick Venture stderr, 2067dcca5ddSPatrick Venture "Committing to verification file to trigger verification service\n"); 207*55646decSPatrick Venture 2087dcca5ddSPatrick Venture try 2097dcca5ddSPatrick Venture { 2107dcca5ddSPatrick Venture blob->commit(session, {}); 2117dcca5ddSPatrick Venture } 2127dcca5ddSPatrick Venture catch (const ipmiblob::BlobException& b) 2137dcca5ddSPatrick Venture { 2147dcca5ddSPatrick Venture throw ToolException("blob exception received: " + 2157dcca5ddSPatrick Venture std::string(b.what())); 2167dcca5ddSPatrick Venture } 2177dcca5ddSPatrick Venture 218d61b0ff8SPatrick Venture std::fprintf(stderr, 219d61b0ff8SPatrick Venture "Calling stat on verification session to check status\n"); 2207dcca5ddSPatrick Venture 221d61b0ff8SPatrick Venture if (pollVerificationStatus(session, blob)) 222d61b0ff8SPatrick Venture { 223d61b0ff8SPatrick Venture std::fprintf(stderr, "Verification returned success\n"); 224*55646decSPatrick Venture success = true; 225d61b0ff8SPatrick Venture } 226d61b0ff8SPatrick Venture else 227d61b0ff8SPatrick Venture { 228d61b0ff8SPatrick Venture std::fprintf(stderr, "Verification returned non-success (could still " 229d61b0ff8SPatrick Venture "be running (unlikely))\n"); 230d61b0ff8SPatrick Venture } 231d61b0ff8SPatrick Venture 2327dcca5ddSPatrick Venture blob->closeBlob(session); 233*55646decSPatrick Venture return (success == true); 234*55646decSPatrick Venture } 235*55646decSPatrick Venture 236*55646decSPatrick Venture void updaterMain(UpdateHandler* updater, const std::string& imagePath, 237*55646decSPatrick Venture const std::string& signaturePath) 238*55646decSPatrick Venture { 239*55646decSPatrick Venture /* TODO(venture): Add optional parameter to specify the flash type, default 240*55646decSPatrick Venture * to legacy for now. 241*55646decSPatrick Venture * 242*55646decSPatrick Venture * TODO(venture): Move the strings from the FirmwareHandler object to a 243*55646decSPatrick Venture * boring utils object so it will be more happly linked cleanly to both the 244*55646decSPatrick Venture * BMC and host-side. 245*55646decSPatrick Venture */ 246*55646decSPatrick Venture std::string goalFirmware = "/flash/image"; 247*55646decSPatrick Venture std::string hashFilename = "/flash/hash"; 248*55646decSPatrick Venture std::string verifyFilename = "/flash/verify"; 249*55646decSPatrick Venture 250*55646decSPatrick Venture bool goalSupported = updater->checkAvailable(goalFirmware); 251*55646decSPatrick Venture if (!goalSupported) 252*55646decSPatrick Venture { 253*55646decSPatrick Venture throw ToolException("Goal firmware or interface not supported"); 254*55646decSPatrick Venture } 255*55646decSPatrick Venture 256*55646decSPatrick Venture /* Yay, our data handler is supported. */ 257*55646decSPatrick Venture 258*55646decSPatrick Venture /* Send over the firmware image. */ 259*55646decSPatrick Venture std::fprintf(stderr, "Sending over the firmware image.\n"); 260*55646decSPatrick Venture updater->sendFile(goalFirmware, imagePath); 261*55646decSPatrick Venture 262*55646decSPatrick Venture /* Send over the hash contents. */ 263*55646decSPatrick Venture std::fprintf(stderr, "Sending over the hash file.\n"); 264*55646decSPatrick Venture updater->sendFile(hashFilename, signaturePath); 265*55646decSPatrick Venture 266*55646decSPatrick Venture /* Trigger the verification by opening the verify file. */ 267*55646decSPatrick Venture std::fprintf(stderr, "Opening the verification file\n"); 268*55646decSPatrick Venture if (updater->verifyFile(verifyFilename)) 269*55646decSPatrick Venture { 270*55646decSPatrick Venture std::fprintf(stderr, "succeeded\n"); 271*55646decSPatrick Venture } 272*55646decSPatrick Venture else 273*55646decSPatrick Venture { 274*55646decSPatrick Venture std::fprintf(stderr, "failed\n"); 275*55646decSPatrick Venture } 276bf58cd64SPatrick Venture } 2779b534f06SPatrick Venture 2789b534f06SPatrick Venture } // namespace host_tool 279