blob: 92afe5d57fc26b99d2a785f7067b8d3d06e84e16 [file] [log] [blame]
Patrick Venturebf58cd62018-12-11 09:05:46 -08001/*
2 * Copyright 2018 Google Inc.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "updater.hpp"
18
Patrick Ventured61b0ff2019-05-15 15:58:06 -070019#include "firmware_handler.hpp"
Patrick Venture2bc23fe2018-12-13 10:16:36 -080020#include "tool_errors.hpp"
Patrick Venture0533d0b2018-12-13 08:48:24 -080021
Patrick Venture00887592018-12-11 10:57:06 -080022#include <algorithm>
Patrick Venture664c5bc2019-03-07 08:09:45 -080023#include <blobs-ipmid/blobs.hpp>
Patrick Venture339dece2018-12-14 18:32:04 -080024#include <cstring>
Patrick Venture664c5bc2019-03-07 08:09:45 -080025#include <ipmiblob/blob_errors.hpp>
Patrick Ventureaf696252018-12-11 10:22:14 -080026#include <memory>
Patrick Venture2a927e82019-02-01 07:29:47 -080027#include <string>
Patrick Ventured61b0ff2019-05-15 15:58:06 -070028#include <thread>
Patrick Venture55646de2019-05-16 10:06:26 -070029#include <vector>
Patrick Ventureaf696252018-12-11 10:22:14 -080030
Patrick Venture9b534f02018-12-13 16:10:02 -080031namespace host_tool
32{
33
Patrick Venture55646de2019-05-16 10:06:26 -070034bool UpdateHandler::checkAvailable(const std::string& goalFirmware)
35{
36 std::vector<std::string> blobs = blob->getBlobList();
37
38 auto blobInst = std::find_if(
39 blobs.begin(), blobs.end(), [&goalFirmware](const std::string& iter) {
40 /* Running into weird scenarios where the string comparison doesn't
41 * work. TODO: revisit.
42 */
43 return (0 == std::memcmp(goalFirmware.c_str(), iter.c_str(),
44 goalFirmware.length()));
45 // return (goalFirmware.compare(iter));
46 });
47 if (blobInst == blobs.end())
48 {
49 std::fprintf(stderr, "%s not found\n", goalFirmware.c_str());
50 return false;
51 }
52
53 /* Call stat on /flash/image (or /flash/tarball) and check if data interface
54 * is supported.
55 */
56 ipmiblob::StatResponse stat;
57
58 try
59 {
60 stat = blob->getStat(goalFirmware);
61 }
62 catch (const ipmiblob::BlobException& b)
63 {
64 std::fprintf(stderr, "Received exception '%s' on getStat\n", b.what());
65 return false;
66 }
67
68 auto supported = handler->supportedType();
69 if ((stat.blob_state & supported) == 0)
70 {
71 std::fprintf(stderr, "data interface selected not supported.\n");
72 return false;
73 }
74
75 return true;
76}
77
78void UpdateHandler::sendFile(const std::string& target, const std::string& path)
79{
80 std::uint16_t session;
81 auto supported = handler->supportedType();
82
83 try
84 {
85 session = blob->openBlob(
86 target, static_cast<std::uint16_t>(supported) |
87 static_cast<std::uint16_t>(blobs::OpenFlags::write));
88 }
89 catch (const ipmiblob::BlobException& b)
90 {
91 throw ToolException("blob exception received: " +
92 std::string(b.what()));
93 }
94
95 if (!handler->sendContents(path, session))
96 {
97 /* Need to close the session on failure, or it's stuck open (until the
98 * blob handler timeout is implemented, and even then, why make it wait.
99 */
100 blob->closeBlob(session);
101 throw ToolException("Failed to send contents of " + path);
102 }
103
104 blob->closeBlob(session);
105}
106
Patrick Ventured61b0ff2019-05-15 15:58:06 -0700107/* Poll an open verification session. Handling closing the session is not yet
108 * owned by this method. */
109bool pollVerificationStatus(std::uint16_t session,
110 ipmiblob::BlobInterface* blob)
111{
112 using namespace std::chrono_literals;
113
114 static constexpr auto verificationSleep = 5s;
115 static constexpr int commandAttempts = 20;
116 int attempts = 0;
117 bool exitLoop = false;
118 blobs::FirmwareBlobHandler::VerifyCheckResponses result =
119 blobs::FirmwareBlobHandler::VerifyCheckResponses::other;
120
121 try
122 {
123 /* Reach back the current status from the verification service output.
124 */
125 while (attempts++ < commandAttempts)
126 {
127 ipmiblob::StatResponse resp = blob->getStat(session);
128
129 if (resp.metadata.size() != sizeof(std::uint8_t))
130 {
131 /* TODO: How do we want to handle the verification failures,
132 * because closing the session to the verify blob has a special
133 * as-of-yet not fully defined behavior.
134 */
135 std::fprintf(stderr, "Received invalid metadata response!!!\n");
136 }
137
138 result =
139 static_cast<blobs::FirmwareBlobHandler::VerifyCheckResponses>(
140 resp.metadata[0]);
141
142 switch (result)
143 {
144 case blobs::FirmwareBlobHandler::VerifyCheckResponses::failed:
145 std::fprintf(stderr, "failed\n");
146 exitLoop = true;
147 break;
148 case blobs::FirmwareBlobHandler::VerifyCheckResponses::other:
149 std::fprintf(stderr, "other\n");
150 break;
151 case blobs::FirmwareBlobHandler::VerifyCheckResponses::running:
152 std::fprintf(stderr, "running\n");
153 break;
154 case blobs::FirmwareBlobHandler::VerifyCheckResponses::success:
155 std::fprintf(stderr, "success\n");
156 exitLoop = true;
157 break;
158 default:
159 std::fprintf(stderr, "wat\n");
160 }
161
162 if (exitLoop)
163 {
164 break;
165 }
166 std::this_thread::sleep_for(verificationSleep);
167 }
168 }
169 catch (const ipmiblob::BlobException& b)
170 {
171 throw ToolException("blob exception received: " +
172 std::string(b.what()));
173 }
174
175 /* TODO: If this is reached and it's not success, it may be worth just
176 * throwing a ToolException with a timeout message specifying the final
177 * read's value.
178 *
179 * TODO: Given that excepting from certain points leaves the BMC update
180 * state machine in an inconsistent state, we need to carefully evaluate
181 * which exceptions from the lower layers allow one to try and delete the
182 * blobs to rollback the state and progress.
183 */
184 return (result ==
185 blobs::FirmwareBlobHandler::VerifyCheckResponses::success);
186}
187
Patrick Venture55646de2019-05-16 10:06:26 -0700188bool UpdateHandler::verifyFile(const std::string& target)
Patrick Venturebf58cd62018-12-11 09:05:46 -0800189{
Patrick Venture0533d0b2018-12-13 08:48:24 -0800190 std::uint16_t session;
Patrick Venture55646de2019-05-16 10:06:26 -0700191 bool success = false;
192
Patrick Venture0533d0b2018-12-13 08:48:24 -0800193 try
194 {
Patrick Venture664c5bc2019-03-07 08:09:45 -0800195 session = blob->openBlob(
Patrick Venture55646de2019-05-16 10:06:26 -0700196 target, static_cast<std::uint16_t>(blobs::OpenFlags::write));
Patrick Venture7dcca5d2019-05-15 12:32:33 -0700197 }
198 catch (const ipmiblob::BlobException& b)
199 {
200 throw ToolException("blob exception received: " +
201 std::string(b.what()));
202 }
203
204 std::fprintf(
205 stderr,
206 "Committing to verification file to trigger verification service\n");
Patrick Venture55646de2019-05-16 10:06:26 -0700207
Patrick Venture7dcca5d2019-05-15 12:32:33 -0700208 try
209 {
210 blob->commit(session, {});
211 }
212 catch (const ipmiblob::BlobException& b)
213 {
214 throw ToolException("blob exception received: " +
215 std::string(b.what()));
216 }
217
Patrick Ventured61b0ff2019-05-15 15:58:06 -0700218 std::fprintf(stderr,
219 "Calling stat on verification session to check status\n");
Patrick Venture7dcca5d2019-05-15 12:32:33 -0700220
Patrick Ventured61b0ff2019-05-15 15:58:06 -0700221 if (pollVerificationStatus(session, blob))
222 {
223 std::fprintf(stderr, "Verification returned success\n");
Patrick Venture55646de2019-05-16 10:06:26 -0700224 success = true;
Patrick Ventured61b0ff2019-05-15 15:58:06 -0700225 }
226 else
227 {
228 std::fprintf(stderr, "Verification returned non-success (could still "
229 "be running (unlikely))\n");
230 }
231
Patrick Venture7dcca5d2019-05-15 12:32:33 -0700232 blob->closeBlob(session);
Patrick Venture55646de2019-05-16 10:06:26 -0700233 return (success == true);
234}
235
236void updaterMain(UpdateHandler* updater, const std::string& imagePath,
237 const std::string& signaturePath)
238{
239 /* TODO(venture): Add optional parameter to specify the flash type, default
240 * to legacy for now.
241 *
242 * TODO(venture): Move the strings from the FirmwareHandler object to a
243 * boring utils object so it will be more happly linked cleanly to both the
244 * BMC and host-side.
245 */
246 std::string goalFirmware = "/flash/image";
247 std::string hashFilename = "/flash/hash";
248 std::string verifyFilename = "/flash/verify";
249
250 bool goalSupported = updater->checkAvailable(goalFirmware);
251 if (!goalSupported)
252 {
253 throw ToolException("Goal firmware or interface not supported");
254 }
255
256 /* Yay, our data handler is supported. */
257
258 /* Send over the firmware image. */
259 std::fprintf(stderr, "Sending over the firmware image.\n");
260 updater->sendFile(goalFirmware, imagePath);
261
262 /* Send over the hash contents. */
263 std::fprintf(stderr, "Sending over the hash file.\n");
264 updater->sendFile(hashFilename, signaturePath);
265
266 /* Trigger the verification by opening the verify file. */
267 std::fprintf(stderr, "Opening the verification file\n");
268 if (updater->verifyFile(verifyFilename))
269 {
270 std::fprintf(stderr, "succeeded\n");
271 }
272 else
273 {
274 std::fprintf(stderr, "failed\n");
275 }
Patrick Venturebf58cd62018-12-11 09:05:46 -0800276}
Patrick Venture9b534f02018-12-13 16:10:02 -0800277
278} // namespace host_tool