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