tools: implement firmware verification polling
The verification process is asynchronous, therefore poll the BMC
firmware state.
Tested: Verified it behaves as intended, before moving it into a
sub-routine:
Opening the verification file
Committing to verification file to trigger verification service
Calling stat on verification session to check status
stat received:
blob_state: 0x206
size: 0x0
metadata(1): 0x3
other
stat received:
blob_state: 0x206
size: 0x0
metadata(1): 0x0
running
stat received:
blob_state: 0x206
size: 0x0
metadata(1): 0x0
running
stat received:
blob_state: 0x206
size: 0x0
metadata(1): 0x1
success
stat received:
blob_state: 0x206
size: 0x0
metadata(1): 0x1
success
Signed-off-by: Patrick Venture <venture@google.com>
Change-Id: I3cb13d1a966c2c833fd4fee5654332f34e80355a
diff --git a/tools/updater.cpp b/tools/updater.cpp
index fd72136..7c78765 100644
--- a/tools/updater.cpp
+++ b/tools/updater.cpp
@@ -16,6 +16,7 @@
#include "updater.hpp"
+#include "firmware_handler.hpp"
#include "tool_errors.hpp"
#include <algorithm>
@@ -24,10 +25,92 @@
#include <ipmiblob/blob_errors.hpp>
#include <memory>
#include <string>
+#include <thread>
namespace host_tool
{
+/* Poll an open verification session. Handling closing the session is not yet
+ * owned by this method. */
+bool pollVerificationStatus(std::uint16_t session,
+ ipmiblob::BlobInterface* blob)
+{
+ using namespace std::chrono_literals;
+
+ static constexpr auto verificationSleep = 5s;
+ static constexpr int commandAttempts = 20;
+ int attempts = 0;
+ bool exitLoop = false;
+ blobs::FirmwareBlobHandler::VerifyCheckResponses result =
+ blobs::FirmwareBlobHandler::VerifyCheckResponses::other;
+
+ try
+ {
+ /* Reach back the current status from the verification service output.
+ */
+ while (attempts++ < commandAttempts)
+ {
+ ipmiblob::StatResponse resp = blob->getStat(session);
+
+ if (resp.metadata.size() != sizeof(std::uint8_t))
+ {
+ /* TODO: How do we want to handle the verification failures,
+ * because closing the session to the verify blob has a special
+ * as-of-yet not fully defined behavior.
+ */
+ std::fprintf(stderr, "Received invalid metadata response!!!\n");
+ }
+
+ result =
+ static_cast<blobs::FirmwareBlobHandler::VerifyCheckResponses>(
+ resp.metadata[0]);
+
+ switch (result)
+ {
+ case blobs::FirmwareBlobHandler::VerifyCheckResponses::failed:
+ std::fprintf(stderr, "failed\n");
+ exitLoop = true;
+ break;
+ case blobs::FirmwareBlobHandler::VerifyCheckResponses::other:
+ std::fprintf(stderr, "other\n");
+ break;
+ case blobs::FirmwareBlobHandler::VerifyCheckResponses::running:
+ std::fprintf(stderr, "running\n");
+ break;
+ case blobs::FirmwareBlobHandler::VerifyCheckResponses::success:
+ std::fprintf(stderr, "success\n");
+ exitLoop = true;
+ break;
+ default:
+ std::fprintf(stderr, "wat\n");
+ }
+
+ if (exitLoop)
+ {
+ break;
+ }
+ std::this_thread::sleep_for(verificationSleep);
+ }
+ }
+ catch (const ipmiblob::BlobException& b)
+ {
+ throw ToolException("blob exception received: " +
+ std::string(b.what()));
+ }
+
+ /* TODO: If this is reached and it's not success, it may be worth just
+ * throwing a ToolException with a timeout message specifying the final
+ * read's value.
+ *
+ * TODO: Given that excepting from certain points leaves the BMC update
+ * state machine in an inconsistent state, we need to carefully evaluate
+ * which exceptions from the lower layers allow one to try and delete the
+ * blobs to rollback the state and progress.
+ */
+ return (result ==
+ blobs::FirmwareBlobHandler::VerifyCheckResponses::success);
+}
+
void updaterMain(ipmiblob::BlobInterface* blob, DataInterface* handler,
const std::string& imagePath, const std::string& signaturePath)
{
@@ -162,9 +245,24 @@
std::string(b.what()));
}
- /* TODO: Check the verification via stat(session). */
+ std::fprintf(stderr,
+ "Calling stat on verification session to check status\n");
- /* DO NOT CLOSE the verification session until it's done. */
+ if (pollVerificationStatus(session, blob))
+ {
+ std::fprintf(stderr, "Verification returned success\n");
+ }
+ else
+ {
+ std::fprintf(stderr, "Verification returned non-success (could still "
+ "be running (unlikely))\n");
+ }
+
+ /* DO NOT CLOSE the verification session until it's done.
+ * TODO: Evaluate what closing verification should do? If the process is
+ * complete, nothing bad, maybe reset the entire state machine? This will
+ * benefit from a diagram.
+ */
blob->closeBlob(session);
return;