blob: 55503b92d0c47bdbee2c9d1e53d2fbb2c6d4afc8 [file] [log] [blame]
Patrick Venture01123b22019-06-20 13:49:06 -07001/*
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 "handler.hpp"
18
Patrick Venture84778b82019-06-26 20:11:09 -070019#include "flags.hpp"
Patrick Venture01123b22019-06-20 13:49:06 -070020#include "helper.hpp"
21#include "status.hpp"
22#include "tool_errors.hpp"
23#include "util.hpp"
24
Patrick Venture9b37b092020-05-28 20:58:57 -070025#include <ipmiblob/blob_errors.hpp>
Willy Tu3f596282024-04-12 16:27:17 +000026#include <stdplus/function_view.hpp>
William A. Kennington III43eeeb32021-10-14 01:49:34 -070027#include <stdplus/handle/managed.hpp>
Patrick Venture9b37b092020-05-28 20:58:57 -070028
Patrick Venture01123b22019-06-20 13:49:06 -070029#include <algorithm>
Patrick Venture01123b22019-06-20 13:49:06 -070030#include <cstdint>
31#include <cstring>
Patrick Venture01123b22019-06-20 13:49:06 -070032#include <string>
33#include <vector>
34
35namespace host_tool
36{
37
William A. Kennington III43eeeb32021-10-14 01:49:34 -070038static void closeBlob(uint16_t&& session, ipmiblob::BlobInterface*& blob)
39{
40 blob->closeBlob(session);
41}
42
43using BlobHandle =
44 stdplus::Managed<uint16_t, ipmiblob::BlobInterface*>::Handle<closeBlob>;
45
46template <typename... Args>
47inline BlobHandle openBlob(ipmiblob::BlobInterface* blob, Args&&... args)
48{
49 return BlobHandle(blob->openBlob(std::forward<Args>(args)...), blob);
50}
51
Patrick Venture01123b22019-06-20 13:49:06 -070052bool UpdateHandler::checkAvailable(const std::string& goalFirmware)
53{
54 std::vector<std::string> blobs = blob->getBlobList();
55
Patrick Williams42a44c22024-08-16 15:21:32 -040056 auto blobInst = std::find_if(
57 blobs.begin(), blobs.end(), [&goalFirmware](const std::string& iter) {
58 /* Running into weird scenarios where the string comparison doesn't
59 * work. TODO: revisit.
60 */
61 return (0 == std::memcmp(goalFirmware.c_str(), iter.c_str(),
62 goalFirmware.length()));
63 // return (goalFirmware.compare(iter));
64 });
Patrick Venture01123b22019-06-20 13:49:06 -070065 if (blobInst == blobs.end())
66 {
67 std::fprintf(stderr, "%s not found\n", goalFirmware.c_str());
68 return false;
69 }
70
Patrick Venture01123b22019-06-20 13:49:06 -070071 return true;
72}
73
Willy Tu3f596282024-04-12 16:27:17 +000074std::vector<uint8_t> UpdateHandler::retryIfFailed(
75 stdplus::function_view<std::vector<uint8_t>()> callback)
76{
77 constexpr uint8_t retryCount = 3;
78 uint8_t i = 1;
79 while (true)
80 {
81 try
82 {
83 return callback();
84 }
85 catch (const ipmiblob::BlobException& b)
86 {
Patrick Williams42a44c22024-08-16 15:21:32 -040087 throw ToolException(
88 "blob exception received: " + std::string(b.what()));
Willy Tu3f596282024-04-12 16:27:17 +000089 }
90 catch (const ToolException& t)
91 {
92 uint8_t remains = retryCount - i;
93 std::fprintf(
94 stderr,
95 "tool exception received: %s: Retrying it %u more times\n",
96 t.what(), remains);
97 if (remains == 0)
98 throw;
99 }
100 ++i;
101 handler->waitForRetry();
102 }
103 return {};
104}
105
Willy Tu1b23b772023-03-15 01:33:03 -0700106void UpdateHandler::retrySendFile(const std::string& target,
107 const std::string& path)
Patrick Venture01123b22019-06-20 13:49:06 -0700108{
Patrick Venture01123b22019-06-20 13:49:06 -0700109 auto supported = handler->supportedType();
Willy Tu1b23b772023-03-15 01:33:03 -0700110 auto session =
111 openBlob(blob, target,
112 static_cast<std::uint16_t>(supported) |
113 static_cast<std::uint16_t>(
114 ipmi_flash::FirmwareFlags::UpdateFlags::openWrite));
Patrick Venture01123b22019-06-20 13:49:06 -0700115
Willy Tu1b23b772023-03-15 01:33:03 -0700116 if (!handler->sendContents(path, *session))
Patrick Venture01123b22019-06-20 13:49:06 -0700117 {
Willy Tu1b23b772023-03-15 01:33:03 -0700118 throw ToolException("Failed to send contents of " + path);
Patrick Venture01123b22019-06-20 13:49:06 -0700119 }
Willy Tu1b23b772023-03-15 01:33:03 -0700120}
121
122void UpdateHandler::sendFile(const std::string& target, const std::string& path)
123{
Willy Tu3f596282024-04-12 16:27:17 +0000124 retryIfFailed([this, target, path]() {
125 this->retrySendFile(target, path);
126 return std::vector<uint8_t>{};
127 });
128}
129
130void UpdateHandler::retryVerifyFile(const std::string& target,
131 bool ignoreStatus)
132{
133 auto session =
134 openBlob(blob, target,
135 static_cast<std::uint16_t>(
136 ipmi_flash::FirmwareFlags::UpdateFlags::openWrite));
137
138 std::fprintf(stderr, "Committing to %s to trigger service\n",
139 target.c_str());
140 blob->commit(*session, {});
141
142 if (ignoreStatus)
Patrick Venture01123b22019-06-20 13:49:06 -0700143 {
Willy Tu3f596282024-04-12 16:27:17 +0000144 // Skip checking the blob for status if ignoreStatus is enabled
145 return;
Patrick Venture01123b22019-06-20 13:49:06 -0700146 }
Willy Tu3f596282024-04-12 16:27:17 +0000147
148 std::fprintf(stderr, "Calling stat on %s session to check status\n",
149 target.c_str());
150 pollStatus(*session, blob);
151 return;
Patrick Venture01123b22019-06-20 13:49:06 -0700152}
153
Brandon Kim6749ba12019-09-19 13:31:37 -0700154bool UpdateHandler::verifyFile(const std::string& target, bool ignoreStatus)
Patrick Venture01123b22019-06-20 13:49:06 -0700155{
Willy Tu3f596282024-04-12 16:27:17 +0000156 retryIfFailed([this, target, ignoreStatus]() {
157 this->retryVerifyFile(target, ignoreStatus);
158 return std::vector<uint8_t>{};
159 });
160
161 return true;
162}
163
164std::vector<uint8_t>
165 UpdateHandler::retryReadVersion(const std::string& versionBlob)
166{
167 auto session =
168 openBlob(blob, versionBlob,
169 static_cast<std::uint16_t>(
170 ipmi_flash::FirmwareFlags::UpdateFlags::openRead));
171
172 std::fprintf(stderr, "Calling stat on %s session to check status\n",
173 versionBlob.c_str());
174
175 /* TODO: call readBytes multiple times in case IPMI message length
176 * exceeds IPMI_MAX_MSG_LENGTH.
177 */
178 auto size = pollReadReady(*session, blob);
179 if (size > 0)
Patrick Venture01123b22019-06-20 13:49:06 -0700180 {
Willy Tu3f596282024-04-12 16:27:17 +0000181 return blob->readBytes(*session, 0, size);
Patrick Venture01123b22019-06-20 13:49:06 -0700182 }
Willy Tu3f596282024-04-12 16:27:17 +0000183 return {};
Patrick Venture01123b22019-06-20 13:49:06 -0700184}
185
Jie Yang328f5202021-03-16 00:52:07 -0700186std::vector<uint8_t> UpdateHandler::readVersion(const std::string& versionBlob)
187{
Patrick Williams42a44c22024-08-16 15:21:32 -0400188 return retryIfFailed([this, versionBlob]() {
189 return retryReadVersion(versionBlob);
190 });
Jie Yang328f5202021-03-16 00:52:07 -0700191}
192
Patrick Venture01123b22019-06-20 13:49:06 -0700193void UpdateHandler::cleanArtifacts()
194{
Patrick Venture01123b22019-06-20 13:49:06 -0700195 /* Errors aren't important for this call. */
196 try
197 {
William A. Kennington III4b0c2ec2021-10-14 02:04:14 -0700198 std::fprintf(stderr, "Executing cleanup blob\n");
William A. Kennington III43eeeb32021-10-14 01:49:34 -0700199 auto session =
200 openBlob(blob, ipmi_flash::cleanupBlobId,
201 static_cast<std::uint16_t>(
202 ipmi_flash::FirmwareFlags::UpdateFlags::openWrite));
William A. Kennington III43eeeb32021-10-14 01:49:34 -0700203 blob->commit(*session, {});
Patrick Venture01123b22019-06-20 13:49:06 -0700204 }
William A. Kennington III4b0c2ec2021-10-14 02:04:14 -0700205 catch (const std::exception& e)
206 {
207 std::fprintf(stderr, "Cleanup failed: %s\n", e.what());
208 }
Patrick Venture01123b22019-06-20 13:49:06 -0700209}
210
211} // namespace host_tool