blob: 7c78765dd81d01e354bc719578df1f39f42501ce [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 Ventureaf696252018-12-11 10:22:14 -080029
Patrick Venture9b534f02018-12-13 16:10:02 -080030namespace host_tool
31{
32
Patrick Ventured61b0ff2019-05-15 15:58:06 -070033/* Poll an open verification session. Handling closing the session is not yet
34 * owned by this method. */
35bool pollVerificationStatus(std::uint16_t session,
36 ipmiblob::BlobInterface* blob)
37{
38 using namespace std::chrono_literals;
39
40 static constexpr auto verificationSleep = 5s;
41 static constexpr int commandAttempts = 20;
42 int attempts = 0;
43 bool exitLoop = false;
44 blobs::FirmwareBlobHandler::VerifyCheckResponses result =
45 blobs::FirmwareBlobHandler::VerifyCheckResponses::other;
46
47 try
48 {
49 /* Reach back the current status from the verification service output.
50 */
51 while (attempts++ < commandAttempts)
52 {
53 ipmiblob::StatResponse resp = blob->getStat(session);
54
55 if (resp.metadata.size() != sizeof(std::uint8_t))
56 {
57 /* TODO: How do we want to handle the verification failures,
58 * because closing the session to the verify blob has a special
59 * as-of-yet not fully defined behavior.
60 */
61 std::fprintf(stderr, "Received invalid metadata response!!!\n");
62 }
63
64 result =
65 static_cast<blobs::FirmwareBlobHandler::VerifyCheckResponses>(
66 resp.metadata[0]);
67
68 switch (result)
69 {
70 case blobs::FirmwareBlobHandler::VerifyCheckResponses::failed:
71 std::fprintf(stderr, "failed\n");
72 exitLoop = true;
73 break;
74 case blobs::FirmwareBlobHandler::VerifyCheckResponses::other:
75 std::fprintf(stderr, "other\n");
76 break;
77 case blobs::FirmwareBlobHandler::VerifyCheckResponses::running:
78 std::fprintf(stderr, "running\n");
79 break;
80 case blobs::FirmwareBlobHandler::VerifyCheckResponses::success:
81 std::fprintf(stderr, "success\n");
82 exitLoop = true;
83 break;
84 default:
85 std::fprintf(stderr, "wat\n");
86 }
87
88 if (exitLoop)
89 {
90 break;
91 }
92 std::this_thread::sleep_for(verificationSleep);
93 }
94 }
95 catch (const ipmiblob::BlobException& b)
96 {
97 throw ToolException("blob exception received: " +
98 std::string(b.what()));
99 }
100
101 /* TODO: If this is reached and it's not success, it may be worth just
102 * throwing a ToolException with a timeout message specifying the final
103 * read's value.
104 *
105 * TODO: Given that excepting from certain points leaves the BMC update
106 * state machine in an inconsistent state, we need to carefully evaluate
107 * which exceptions from the lower layers allow one to try and delete the
108 * blobs to rollback the state and progress.
109 */
110 return (result ==
111 blobs::FirmwareBlobHandler::VerifyCheckResponses::success);
112}
113
Patrick Venture664c5bc2019-03-07 08:09:45 -0800114void updaterMain(ipmiblob::BlobInterface* blob, DataInterface* handler,
Patrick Venture2bc23fe2018-12-13 10:16:36 -0800115 const std::string& imagePath, const std::string& signaturePath)
Patrick Venturebf58cd62018-12-11 09:05:46 -0800116{
Patrick Ventureaf696252018-12-11 10:22:14 -0800117 /* TODO(venture): Add optional parameter to specify the flash type, default
118 * to legacy for now.
Patrick Venture7dcca5d2019-05-15 12:32:33 -0700119 *
120 * TODO(venture): Move the strings from the FirmwareHandler object to a
121 * boring utils object so it will be more happly linked cleanly to both the
122 * BMC and host-side.
Patrick Ventureaf696252018-12-11 10:22:14 -0800123 */
Patrick Venture00887592018-12-11 10:57:06 -0800124 std::string goalFirmware = "/flash/image";
Patrick Venture73528382019-05-14 12:43:37 -0700125 std::string hashFilename = "/flash/hash";
Patrick Venture7dcca5d2019-05-15 12:32:33 -0700126 std::string verifyFilename = "/flash/verify";
Patrick Venture00887592018-12-11 10:57:06 -0800127
Patrick Venture0bf8bf02018-12-12 20:43:25 -0800128 /* Get list of blob_ids, check for /flash/image, or /flash/tarball.
129 * TODO(venture) the mechanism doesn't care, but the caller of burn_my_bmc
130 * will have in mind which they're sending and we need to verify it's
131 * available and use it.
132 */
Patrick Venture00887592018-12-11 10:57:06 -0800133 std::vector<std::string> blobs = blob->getBlobList();
Patrick Venture339dece2018-12-14 18:32:04 -0800134 auto blobInst = std::find_if(
Patrick Venture2a927e82019-02-01 07:29:47 -0800135 blobs.begin(), blobs.end(), [&goalFirmware](const std::string& iter) {
Patrick Venture339dece2018-12-14 18:32:04 -0800136 /* Running into weird scenarios where the string comparison doesn't
137 * work. TODO: revisit.
138 */
139 return (0 == std::memcmp(goalFirmware.c_str(), iter.c_str(),
140 goalFirmware.length()));
141 // return (goalFirmware.compare(iter));
142 });
Patrick Venture00887592018-12-11 10:57:06 -0800143 if (blobInst == blobs.end())
144 {
Patrick Venture2bc23fe2018-12-13 10:16:36 -0800145 throw ToolException(goalFirmware + " not found");
Patrick Venture00887592018-12-11 10:57:06 -0800146 }
Patrick Ventureaf696252018-12-11 10:22:14 -0800147
Patrick Ventureaf696252018-12-11 10:22:14 -0800148 /* Call stat on /flash/image (or /flash/tarball) and check if data interface
Patrick Venture00887592018-12-11 10:57:06 -0800149 * is supported.
150 */
Patrick Venture664c5bc2019-03-07 08:09:45 -0800151 ipmiblob::StatResponse stat;
Patrick Venture339dece2018-12-14 18:32:04 -0800152 try
153 {
154 stat = blob->getStat(goalFirmware);
155 }
Patrick Venture664c5bc2019-03-07 08:09:45 -0800156 catch (const ipmiblob::BlobException& b)
Patrick Venture339dece2018-12-14 18:32:04 -0800157 {
158 throw ToolException("blob exception received: " +
159 std::string(b.what()));
160 }
161
Patrick Ventureaa32a362018-12-13 10:52:33 -0800162 auto supported = handler->supportedType();
163 if ((stat.blob_state & supported) == 0)
Patrick Venture8a55dcb2018-12-12 21:12:58 -0800164 {
Patrick Venture2bc23fe2018-12-13 10:16:36 -0800165 throw ToolException("data interface selected not supported.");
Patrick Venture8a55dcb2018-12-12 21:12:58 -0800166 }
Patrick Ventureaf696252018-12-11 10:22:14 -0800167
Patrick Venture0533d0b2018-12-13 08:48:24 -0800168 /* Yay, our data handler is supported. */
Patrick Venture73528382019-05-14 12:43:37 -0700169
170 /* Send over the firmware image. */
171 std::fprintf(stderr, "Sending over the firmware image.\n");
Patrick Venture0533d0b2018-12-13 08:48:24 -0800172 std::uint16_t session;
173 try
174 {
Patrick Venture664c5bc2019-03-07 08:09:45 -0800175 session = blob->openBlob(
176 goalFirmware,
177 static_cast<std::uint16_t>(supported) |
178 static_cast<std::uint16_t>(blobs::OpenFlags::write));
Patrick Venture0533d0b2018-12-13 08:48:24 -0800179 }
Patrick Venture664c5bc2019-03-07 08:09:45 -0800180 catch (const ipmiblob::BlobException& b)
Patrick Venture0533d0b2018-12-13 08:48:24 -0800181 {
Patrick Venture2bc23fe2018-12-13 10:16:36 -0800182 throw ToolException("blob exception received: " +
183 std::string(b.what()));
Patrick Venture0533d0b2018-12-13 08:48:24 -0800184 }
185
Patrick Venturefd6aaec2018-12-13 09:06:02 -0800186 if (!handler->sendContents(imagePath, session))
187 {
Patrick Venturef9566d82019-01-16 09:44:01 -0800188 /* Need to close the session on failure, or it's stuck open (until the
189 * blob handler timeout is implemented, and even then, why make it wait.
190 */
191 blob->closeBlob(session);
Patrick Venture2bc23fe2018-12-13 10:16:36 -0800192 throw ToolException("Failed to send contents of " + imagePath);
Patrick Venturefd6aaec2018-12-13 09:06:02 -0800193 }
194
Patrick Venture9a5ce562018-12-14 18:56:04 -0800195 blob->closeBlob(session);
196
Patrick Venturefd6aaec2018-12-13 09:06:02 -0800197 /* Send over the hash contents. */
Patrick Venture73528382019-05-14 12:43:37 -0700198 std::fprintf(stderr, "Sending over the hash file.\n");
199 try
200 {
201 session = blob->openBlob(
202 hashFilename,
203 static_cast<std::uint16_t>(supported) |
204 static_cast<std::uint16_t>(blobs::OpenFlags::write));
205 }
206 catch (const ipmiblob::BlobException& b)
207 {
208 throw ToolException("blob exception received: " +
209 std::string(b.what()));
210 }
211
212 if (!handler->sendContents(signaturePath, session))
213 {
214 blob->closeBlob(session);
215 throw ToolException("Failed to send contents of " + signaturePath);
216 }
217
218 blob->closeBlob(session);
219
Patrick Venture7dcca5d2019-05-15 12:32:33 -0700220 /* Trigger the verification by opening the verify file. */
221 std::fprintf(stderr, "Opening the verification file\n");
222 try
223 {
224 session = blob->openBlob(
225 verifyFilename,
226 static_cast<std::uint16_t>(supported) |
227 static_cast<std::uint16_t>(blobs::OpenFlags::write));
228 }
229 catch (const ipmiblob::BlobException& b)
230 {
231 throw ToolException("blob exception received: " +
232 std::string(b.what()));
233 }
234
235 std::fprintf(
236 stderr,
237 "Committing to verification file to trigger verification service\n");
238 try
239 {
240 blob->commit(session, {});
241 }
242 catch (const ipmiblob::BlobException& b)
243 {
244 throw ToolException("blob exception received: " +
245 std::string(b.what()));
246 }
247
Patrick Ventured61b0ff2019-05-15 15:58:06 -0700248 std::fprintf(stderr,
249 "Calling stat on verification session to check status\n");
Patrick Venture7dcca5d2019-05-15 12:32:33 -0700250
Patrick Ventured61b0ff2019-05-15 15:58:06 -0700251 if (pollVerificationStatus(session, blob))
252 {
253 std::fprintf(stderr, "Verification returned success\n");
254 }
255 else
256 {
257 std::fprintf(stderr, "Verification returned non-success (could still "
258 "be running (unlikely))\n");
259 }
260
261 /* DO NOT CLOSE the verification session until it's done.
262 * TODO: Evaluate what closing verification should do? If the process is
263 * complete, nothing bad, maybe reset the entire state machine? This will
264 * benefit from a diagram.
265 */
Patrick Venture7dcca5d2019-05-15 12:32:33 -0700266 blob->closeBlob(session);
Patrick Venturefd6aaec2018-12-13 09:06:02 -0800267
Patrick Venture2bc23fe2018-12-13 10:16:36 -0800268 return;
Patrick Venturebf58cd62018-12-11 09:05:46 -0800269}
Patrick Venture9b534f02018-12-13 16:10:02 -0800270
271} // namespace host_tool