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 19*d61b0ff8SPatrick 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> 28*d61b0ff8SPatrick Venture #include <thread> 29af69625fSPatrick Venture 309b534f06SPatrick Venture namespace host_tool 319b534f06SPatrick Venture { 329b534f06SPatrick Venture 33*d61b0ff8SPatrick Venture /* Poll an open verification session. Handling closing the session is not yet 34*d61b0ff8SPatrick Venture * owned by this method. */ 35*d61b0ff8SPatrick Venture bool pollVerificationStatus(std::uint16_t session, 36*d61b0ff8SPatrick Venture ipmiblob::BlobInterface* blob) 37*d61b0ff8SPatrick Venture { 38*d61b0ff8SPatrick Venture using namespace std::chrono_literals; 39*d61b0ff8SPatrick Venture 40*d61b0ff8SPatrick Venture static constexpr auto verificationSleep = 5s; 41*d61b0ff8SPatrick Venture static constexpr int commandAttempts = 20; 42*d61b0ff8SPatrick Venture int attempts = 0; 43*d61b0ff8SPatrick Venture bool exitLoop = false; 44*d61b0ff8SPatrick Venture blobs::FirmwareBlobHandler::VerifyCheckResponses result = 45*d61b0ff8SPatrick Venture blobs::FirmwareBlobHandler::VerifyCheckResponses::other; 46*d61b0ff8SPatrick Venture 47*d61b0ff8SPatrick Venture try 48*d61b0ff8SPatrick Venture { 49*d61b0ff8SPatrick Venture /* Reach back the current status from the verification service output. 50*d61b0ff8SPatrick Venture */ 51*d61b0ff8SPatrick Venture while (attempts++ < commandAttempts) 52*d61b0ff8SPatrick Venture { 53*d61b0ff8SPatrick Venture ipmiblob::StatResponse resp = blob->getStat(session); 54*d61b0ff8SPatrick Venture 55*d61b0ff8SPatrick Venture if (resp.metadata.size() != sizeof(std::uint8_t)) 56*d61b0ff8SPatrick Venture { 57*d61b0ff8SPatrick Venture /* TODO: How do we want to handle the verification failures, 58*d61b0ff8SPatrick Venture * because closing the session to the verify blob has a special 59*d61b0ff8SPatrick Venture * as-of-yet not fully defined behavior. 60*d61b0ff8SPatrick Venture */ 61*d61b0ff8SPatrick Venture std::fprintf(stderr, "Received invalid metadata response!!!\n"); 62*d61b0ff8SPatrick Venture } 63*d61b0ff8SPatrick Venture 64*d61b0ff8SPatrick Venture result = 65*d61b0ff8SPatrick Venture static_cast<blobs::FirmwareBlobHandler::VerifyCheckResponses>( 66*d61b0ff8SPatrick Venture resp.metadata[0]); 67*d61b0ff8SPatrick Venture 68*d61b0ff8SPatrick Venture switch (result) 69*d61b0ff8SPatrick Venture { 70*d61b0ff8SPatrick Venture case blobs::FirmwareBlobHandler::VerifyCheckResponses::failed: 71*d61b0ff8SPatrick Venture std::fprintf(stderr, "failed\n"); 72*d61b0ff8SPatrick Venture exitLoop = true; 73*d61b0ff8SPatrick Venture break; 74*d61b0ff8SPatrick Venture case blobs::FirmwareBlobHandler::VerifyCheckResponses::other: 75*d61b0ff8SPatrick Venture std::fprintf(stderr, "other\n"); 76*d61b0ff8SPatrick Venture break; 77*d61b0ff8SPatrick Venture case blobs::FirmwareBlobHandler::VerifyCheckResponses::running: 78*d61b0ff8SPatrick Venture std::fprintf(stderr, "running\n"); 79*d61b0ff8SPatrick Venture break; 80*d61b0ff8SPatrick Venture case blobs::FirmwareBlobHandler::VerifyCheckResponses::success: 81*d61b0ff8SPatrick Venture std::fprintf(stderr, "success\n"); 82*d61b0ff8SPatrick Venture exitLoop = true; 83*d61b0ff8SPatrick Venture break; 84*d61b0ff8SPatrick Venture default: 85*d61b0ff8SPatrick Venture std::fprintf(stderr, "wat\n"); 86*d61b0ff8SPatrick Venture } 87*d61b0ff8SPatrick Venture 88*d61b0ff8SPatrick Venture if (exitLoop) 89*d61b0ff8SPatrick Venture { 90*d61b0ff8SPatrick Venture break; 91*d61b0ff8SPatrick Venture } 92*d61b0ff8SPatrick Venture std::this_thread::sleep_for(verificationSleep); 93*d61b0ff8SPatrick Venture } 94*d61b0ff8SPatrick Venture } 95*d61b0ff8SPatrick Venture catch (const ipmiblob::BlobException& b) 96*d61b0ff8SPatrick Venture { 97*d61b0ff8SPatrick Venture throw ToolException("blob exception received: " + 98*d61b0ff8SPatrick Venture std::string(b.what())); 99*d61b0ff8SPatrick Venture } 100*d61b0ff8SPatrick Venture 101*d61b0ff8SPatrick Venture /* TODO: If this is reached and it's not success, it may be worth just 102*d61b0ff8SPatrick Venture * throwing a ToolException with a timeout message specifying the final 103*d61b0ff8SPatrick Venture * read's value. 104*d61b0ff8SPatrick Venture * 105*d61b0ff8SPatrick Venture * TODO: Given that excepting from certain points leaves the BMC update 106*d61b0ff8SPatrick Venture * state machine in an inconsistent state, we need to carefully evaluate 107*d61b0ff8SPatrick Venture * which exceptions from the lower layers allow one to try and delete the 108*d61b0ff8SPatrick Venture * blobs to rollback the state and progress. 109*d61b0ff8SPatrick Venture */ 110*d61b0ff8SPatrick Venture return (result == 111*d61b0ff8SPatrick Venture blobs::FirmwareBlobHandler::VerifyCheckResponses::success); 112*d61b0ff8SPatrick Venture } 113*d61b0ff8SPatrick Venture 114664c5bc7SPatrick Venture void updaterMain(ipmiblob::BlobInterface* blob, DataInterface* handler, 11500887597SPatrick Venture const std::string& imagePath, const std::string& signaturePath) 116bf58cd64SPatrick Venture { 117af69625fSPatrick Venture /* TODO(venture): Add optional parameter to specify the flash type, default 118af69625fSPatrick Venture * to legacy for now. 1197dcca5ddSPatrick Venture * 1207dcca5ddSPatrick Venture * TODO(venture): Move the strings from the FirmwareHandler object to a 1217dcca5ddSPatrick Venture * boring utils object so it will be more happly linked cleanly to both the 1227dcca5ddSPatrick Venture * BMC and host-side. 123af69625fSPatrick Venture */ 12400887597SPatrick Venture std::string goalFirmware = "/flash/image"; 12573528388SPatrick Venture std::string hashFilename = "/flash/hash"; 1267dcca5ddSPatrick Venture std::string verifyFilename = "/flash/verify"; 12700887597SPatrick Venture 1280bf8bf0cSPatrick Venture /* Get list of blob_ids, check for /flash/image, or /flash/tarball. 1290bf8bf0cSPatrick Venture * TODO(venture) the mechanism doesn't care, but the caller of burn_my_bmc 1300bf8bf0cSPatrick Venture * will have in mind which they're sending and we need to verify it's 1310bf8bf0cSPatrick Venture * available and use it. 1320bf8bf0cSPatrick Venture */ 13300887597SPatrick Venture std::vector<std::string> blobs = blob->getBlobList(); 134339dece8SPatrick Venture auto blobInst = std::find_if( 1352a927e87SPatrick Venture blobs.begin(), blobs.end(), [&goalFirmware](const std::string& iter) { 136339dece8SPatrick Venture /* Running into weird scenarios where the string comparison doesn't 137339dece8SPatrick Venture * work. TODO: revisit. 138339dece8SPatrick Venture */ 139339dece8SPatrick Venture return (0 == std::memcmp(goalFirmware.c_str(), iter.c_str(), 140339dece8SPatrick Venture goalFirmware.length())); 141339dece8SPatrick Venture // return (goalFirmware.compare(iter)); 142339dece8SPatrick Venture }); 14300887597SPatrick Venture if (blobInst == blobs.end()) 14400887597SPatrick Venture { 1452bc23fe1SPatrick Venture throw ToolException(goalFirmware + " not found"); 14600887597SPatrick Venture } 147af69625fSPatrick Venture 148af69625fSPatrick Venture /* Call stat on /flash/image (or /flash/tarball) and check if data interface 14900887597SPatrick Venture * is supported. 15000887597SPatrick Venture */ 151664c5bc7SPatrick Venture ipmiblob::StatResponse stat; 152339dece8SPatrick Venture try 153339dece8SPatrick Venture { 154339dece8SPatrick Venture stat = blob->getStat(goalFirmware); 155339dece8SPatrick Venture } 156664c5bc7SPatrick Venture catch (const ipmiblob::BlobException& b) 157339dece8SPatrick Venture { 158339dece8SPatrick Venture throw ToolException("blob exception received: " + 159339dece8SPatrick Venture std::string(b.what())); 160339dece8SPatrick Venture } 161339dece8SPatrick Venture 162aa32a36aSPatrick Venture auto supported = handler->supportedType(); 163aa32a36aSPatrick Venture if ((stat.blob_state & supported) == 0) 1648a55dcbdSPatrick Venture { 1652bc23fe1SPatrick Venture throw ToolException("data interface selected not supported."); 1668a55dcbdSPatrick Venture } 167af69625fSPatrick Venture 1680533d0b0SPatrick Venture /* Yay, our data handler is supported. */ 16973528388SPatrick Venture 17073528388SPatrick Venture /* Send over the firmware image. */ 17173528388SPatrick Venture std::fprintf(stderr, "Sending over the firmware image.\n"); 1720533d0b0SPatrick Venture std::uint16_t session; 1730533d0b0SPatrick Venture try 1740533d0b0SPatrick Venture { 175664c5bc7SPatrick Venture session = blob->openBlob( 176664c5bc7SPatrick Venture goalFirmware, 177664c5bc7SPatrick Venture static_cast<std::uint16_t>(supported) | 178664c5bc7SPatrick Venture static_cast<std::uint16_t>(blobs::OpenFlags::write)); 1790533d0b0SPatrick Venture } 180664c5bc7SPatrick Venture catch (const ipmiblob::BlobException& b) 1810533d0b0SPatrick Venture { 1822bc23fe1SPatrick Venture throw ToolException("blob exception received: " + 1832bc23fe1SPatrick Venture std::string(b.what())); 1840533d0b0SPatrick Venture } 1850533d0b0SPatrick Venture 186fd6aaec8SPatrick Venture if (!handler->sendContents(imagePath, session)) 187fd6aaec8SPatrick Venture { 188f9566d88SPatrick Venture /* Need to close the session on failure, or it's stuck open (until the 189f9566d88SPatrick Venture * blob handler timeout is implemented, and even then, why make it wait. 190f9566d88SPatrick Venture */ 191f9566d88SPatrick Venture blob->closeBlob(session); 1922bc23fe1SPatrick Venture throw ToolException("Failed to send contents of " + imagePath); 193fd6aaec8SPatrick Venture } 194fd6aaec8SPatrick Venture 1959a5ce561SPatrick Venture blob->closeBlob(session); 1969a5ce561SPatrick Venture 197fd6aaec8SPatrick Venture /* Send over the hash contents. */ 19873528388SPatrick Venture std::fprintf(stderr, "Sending over the hash file.\n"); 19973528388SPatrick Venture try 20073528388SPatrick Venture { 20173528388SPatrick Venture session = blob->openBlob( 20273528388SPatrick Venture hashFilename, 20373528388SPatrick Venture static_cast<std::uint16_t>(supported) | 20473528388SPatrick Venture static_cast<std::uint16_t>(blobs::OpenFlags::write)); 20573528388SPatrick Venture } 20673528388SPatrick Venture catch (const ipmiblob::BlobException& b) 20773528388SPatrick Venture { 20873528388SPatrick Venture throw ToolException("blob exception received: " + 20973528388SPatrick Venture std::string(b.what())); 21073528388SPatrick Venture } 21173528388SPatrick Venture 21273528388SPatrick Venture if (!handler->sendContents(signaturePath, session)) 21373528388SPatrick Venture { 21473528388SPatrick Venture blob->closeBlob(session); 21573528388SPatrick Venture throw ToolException("Failed to send contents of " + signaturePath); 21673528388SPatrick Venture } 21773528388SPatrick Venture 21873528388SPatrick Venture blob->closeBlob(session); 21973528388SPatrick Venture 2207dcca5ddSPatrick Venture /* Trigger the verification by opening the verify file. */ 2217dcca5ddSPatrick Venture std::fprintf(stderr, "Opening the verification file\n"); 2227dcca5ddSPatrick Venture try 2237dcca5ddSPatrick Venture { 2247dcca5ddSPatrick Venture session = blob->openBlob( 2257dcca5ddSPatrick Venture verifyFilename, 2267dcca5ddSPatrick Venture static_cast<std::uint16_t>(supported) | 2277dcca5ddSPatrick Venture static_cast<std::uint16_t>(blobs::OpenFlags::write)); 2287dcca5ddSPatrick Venture } 2297dcca5ddSPatrick Venture catch (const ipmiblob::BlobException& b) 2307dcca5ddSPatrick Venture { 2317dcca5ddSPatrick Venture throw ToolException("blob exception received: " + 2327dcca5ddSPatrick Venture std::string(b.what())); 2337dcca5ddSPatrick Venture } 2347dcca5ddSPatrick Venture 2357dcca5ddSPatrick Venture std::fprintf( 2367dcca5ddSPatrick Venture stderr, 2377dcca5ddSPatrick Venture "Committing to verification file to trigger verification service\n"); 2387dcca5ddSPatrick Venture try 2397dcca5ddSPatrick Venture { 2407dcca5ddSPatrick Venture blob->commit(session, {}); 2417dcca5ddSPatrick Venture } 2427dcca5ddSPatrick Venture catch (const ipmiblob::BlobException& b) 2437dcca5ddSPatrick Venture { 2447dcca5ddSPatrick Venture throw ToolException("blob exception received: " + 2457dcca5ddSPatrick Venture std::string(b.what())); 2467dcca5ddSPatrick Venture } 2477dcca5ddSPatrick Venture 248*d61b0ff8SPatrick Venture std::fprintf(stderr, 249*d61b0ff8SPatrick Venture "Calling stat on verification session to check status\n"); 2507dcca5ddSPatrick Venture 251*d61b0ff8SPatrick Venture if (pollVerificationStatus(session, blob)) 252*d61b0ff8SPatrick Venture { 253*d61b0ff8SPatrick Venture std::fprintf(stderr, "Verification returned success\n"); 254*d61b0ff8SPatrick Venture } 255*d61b0ff8SPatrick Venture else 256*d61b0ff8SPatrick Venture { 257*d61b0ff8SPatrick Venture std::fprintf(stderr, "Verification returned non-success (could still " 258*d61b0ff8SPatrick Venture "be running (unlikely))\n"); 259*d61b0ff8SPatrick Venture } 260*d61b0ff8SPatrick Venture 261*d61b0ff8SPatrick Venture /* DO NOT CLOSE the verification session until it's done. 262*d61b0ff8SPatrick Venture * TODO: Evaluate what closing verification should do? If the process is 263*d61b0ff8SPatrick Venture * complete, nothing bad, maybe reset the entire state machine? This will 264*d61b0ff8SPatrick Venture * benefit from a diagram. 265*d61b0ff8SPatrick Venture */ 2667dcca5ddSPatrick Venture blob->closeBlob(session); 267fd6aaec8SPatrick Venture 2682bc23fe1SPatrick Venture return; 269bf58cd64SPatrick Venture } 2709b534f06SPatrick Venture 2719b534f06SPatrick Venture } // namespace host_tool 272