1ded66d0fSJason Ling /*
2ded66d0fSJason Ling * Copyright 2018 Google Inc.
3ded66d0fSJason Ling *
4ded66d0fSJason Ling * Licensed under the Apache License, Version 2.0 (the "License");
5ded66d0fSJason Ling * you may not use this file except in compliance with the License.
6ded66d0fSJason Ling * You may obtain a copy of the License at
7ded66d0fSJason Ling *
8ded66d0fSJason Ling * http://www.apache.org/licenses/LICENSE-2.0
9ded66d0fSJason Ling *
10ded66d0fSJason Ling * Unless required by applicable law or agreed to in writing, software
11ded66d0fSJason Ling * distributed under the License is distributed on an "AS IS" BASIS,
12ded66d0fSJason Ling * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13ded66d0fSJason Ling * See the License for the specific language governing permissions and
14ded66d0fSJason Ling * limitations under the License.
15ded66d0fSJason Ling */
16ded66d0fSJason Ling
17ded66d0fSJason Ling #include "firmware_handler.hpp"
18ded66d0fSJason Ling
19ded66d0fSJason Ling #include "data.hpp"
20ded66d0fSJason Ling #include "flags.hpp"
21ded66d0fSJason Ling #include "image_handler.hpp"
22ded66d0fSJason Ling #include "status.hpp"
23ded66d0fSJason Ling #include "util.hpp"
24ded66d0fSJason Ling
25ded66d0fSJason Ling #include <blobs-ipmid/blobs.hpp>
26ded66d0fSJason Ling
27ded66d0fSJason Ling #include <algorithm>
28ded66d0fSJason Ling #include <cstdint>
29ded66d0fSJason Ling #include <cstring>
30ded66d0fSJason Ling #include <fstream>
31ded66d0fSJason Ling #include <memory>
32ded66d0fSJason Ling #include <string>
33ded66d0fSJason Ling #include <vector>
34ded66d0fSJason Ling
35ded66d0fSJason Ling namespace ipmi_flash
36ded66d0fSJason Ling {
37ded66d0fSJason Ling
38ded66d0fSJason Ling std::unique_ptr<blobs::GenericBlobInterface>
CreateFirmwareBlobHandler(std::vector<HandlerPack> && firmwares,std::vector<DataHandlerPack> && transports,ActionMap && actionPacks)39ded66d0fSJason Ling FirmwareBlobHandler::CreateFirmwareBlobHandler(
40ded66d0fSJason Ling std::vector<HandlerPack>&& firmwares,
41ded66d0fSJason Ling std::vector<DataHandlerPack>&& transports, ActionMap&& actionPacks)
42ded66d0fSJason Ling {
43ded66d0fSJason Ling /* There must be at least one in addition to the hash blob handler. */
44ded66d0fSJason Ling if (firmwares.size() < 2)
45ded66d0fSJason Ling {
46ded66d0fSJason Ling std::fprintf(stderr, "Must provide at least two firmware handlers.");
47ded66d0fSJason Ling return nullptr;
48ded66d0fSJason Ling }
49ded66d0fSJason Ling if (transports.empty())
50ded66d0fSJason Ling {
51ded66d0fSJason Ling return nullptr;
52ded66d0fSJason Ling }
53ded66d0fSJason Ling if (actionPacks.empty())
54ded66d0fSJason Ling {
55ded66d0fSJason Ling return nullptr;
56ded66d0fSJason Ling }
57ded66d0fSJason Ling
58ded66d0fSJason Ling std::vector<std::string> blobs;
5999d292a3SWilly Tu blobs.reserve(firmwares.size());
60*42a44c28SPatrick Williams std::for_each(firmwares.begin(), firmwares.end(),
61*42a44c28SPatrick Williams [&blobs](const auto& blob) {
62*42a44c28SPatrick Williams blobs.emplace_back(blob.blobName);
63*42a44c28SPatrick Williams });
64ded66d0fSJason Ling
65ded66d0fSJason Ling if (0 == std::count(blobs.begin(), blobs.end(), hashBlobId))
66ded66d0fSJason Ling {
67ded66d0fSJason Ling return nullptr;
68ded66d0fSJason Ling }
69ded66d0fSJason Ling
70*42a44c28SPatrick Williams return std::make_unique<FirmwareBlobHandler>(
71*42a44c28SPatrick Williams std::move(firmwares), blobs, std::move(transports),
72ded66d0fSJason Ling std::move(actionPacks));
73ded66d0fSJason Ling }
74ded66d0fSJason Ling
75ded66d0fSJason Ling /* Check if the path is in our supported list (or active list). */
canHandleBlob(const std::string & path)76ded66d0fSJason Ling bool FirmwareBlobHandler::canHandleBlob(const std::string& path)
77ded66d0fSJason Ling {
78ded66d0fSJason Ling return (std::count(blobIDs.begin(), blobIDs.end(), path) > 0);
79ded66d0fSJason Ling }
80ded66d0fSJason Ling
81ded66d0fSJason Ling /*
82ded66d0fSJason Ling * Grab the list of supported firmware.
83ded66d0fSJason Ling *
84ded66d0fSJason Ling * If there's an open firmware session, it'll already be present in the
85ded66d0fSJason Ling * list as "/flash/active/image", and if the hash has started,
86ded66d0fSJason Ling * "/flash/active/hash" regardless of mechanism. This is done in the open
87166b4f19SManojkiran Eda * command, no extra work is required here.
88ded66d0fSJason Ling */
getBlobIds()89ded66d0fSJason Ling std::vector<std::string> FirmwareBlobHandler::getBlobIds()
90ded66d0fSJason Ling {
91ded66d0fSJason Ling return blobIDs;
92ded66d0fSJason Ling }
93ded66d0fSJason Ling
94ded66d0fSJason Ling /*
95ded66d0fSJason Ling * Per the design, this mean abort, and this will trigger whatever
96ded66d0fSJason Ling * appropriate actions are required to abort the process.
97ded66d0fSJason Ling */
deleteBlob(const std::string &)98b487eb47SWilly Tu bool FirmwareBlobHandler::deleteBlob(const std::string&)
99ded66d0fSJason Ling {
100ded66d0fSJason Ling switch (state)
101ded66d0fSJason Ling {
102ded66d0fSJason Ling case UpdateState::notYetStarted:
103ded66d0fSJason Ling /* Trying to delete anything at this point has no effect and returns
104ded66d0fSJason Ling * false.
105ded66d0fSJason Ling */
106ded66d0fSJason Ling return false;
107ded66d0fSJason Ling case UpdateState::verificationPending:
108ded66d0fSJason Ling abortProcess();
109ded66d0fSJason Ling return true;
110ded66d0fSJason Ling case UpdateState::updatePending:
111ded66d0fSJason Ling abortProcess();
112ded66d0fSJason Ling return true;
113ded66d0fSJason Ling default:
114ded66d0fSJason Ling break;
115ded66d0fSJason Ling }
116ded66d0fSJason Ling
117ded66d0fSJason Ling return false;
118ded66d0fSJason Ling }
119ded66d0fSJason Ling
120ded66d0fSJason Ling /*
121ded66d0fSJason Ling * Stat on the files will return information such as what supported
122ded66d0fSJason Ling * transport mechanisms are available.
123ded66d0fSJason Ling *
124ded66d0fSJason Ling * Stat on an active file or hash will return information such as the size
125ded66d0fSJason Ling * of the data cached, and any additional pertinent information. The
126ded66d0fSJason Ling * blob_state on the active files will return the state of the update.
127ded66d0fSJason Ling */
stat(const std::string & path,blobs::BlobMeta * meta)128ded66d0fSJason Ling bool FirmwareBlobHandler::stat(const std::string& path, blobs::BlobMeta* meta)
129ded66d0fSJason Ling {
130ded66d0fSJason Ling /* We know we support this path because canHandle is called ahead */
131ded66d0fSJason Ling if (path == verifyBlobId || path == activeImageBlobId ||
132ded66d0fSJason Ling path == activeHashBlobId || path == updateBlobId)
133ded66d0fSJason Ling {
134ded66d0fSJason Ling /* These blobs are placeholders that indicate things, or allow actions,
135ded66d0fSJason Ling * but are not stat-able as-is.
136ded66d0fSJason Ling */
137ded66d0fSJason Ling return false;
138ded66d0fSJason Ling }
139ded66d0fSJason Ling
140ded66d0fSJason Ling /* They are requesting information about the generic blob_id. */
141ded66d0fSJason Ling
142ded66d0fSJason Ling /* Older host tools expect the blobState to contain a bitmask of available
143ded66d0fSJason Ling * transport backends, so report that we support all of them in order to
144ded66d0fSJason Ling * preserve backwards compatibility.
145ded66d0fSJason Ling */
146ded66d0fSJason Ling meta->blobState = transportMask;
147ded66d0fSJason Ling meta->size = 0;
148ded66d0fSJason Ling return true;
149ded66d0fSJason Ling }
150ded66d0fSJason Ling
getActionStatus()151ded66d0fSJason Ling ActionStatus FirmwareBlobHandler::getActionStatus()
152ded66d0fSJason Ling {
153ded66d0fSJason Ling ActionStatus value = ActionStatus::unknown;
154ded66d0fSJason Ling auto* pack = getActionPack();
155ded66d0fSJason Ling
156ded66d0fSJason Ling switch (state)
157ded66d0fSJason Ling {
158ded66d0fSJason Ling case UpdateState::verificationPending:
159ded66d0fSJason Ling value = ActionStatus::unknown;
160ded66d0fSJason Ling break;
161ded66d0fSJason Ling case UpdateState::verificationStarted:
162ded66d0fSJason Ling /* If we got here, there must be data AND a hash, not just a hash,
163ded66d0fSJason Ling * therefore pack will be known. */
164ded66d0fSJason Ling if (!pack)
165ded66d0fSJason Ling {
166ded66d0fSJason Ling break;
167ded66d0fSJason Ling }
168ded66d0fSJason Ling value = pack->verification->status();
169ded66d0fSJason Ling lastVerificationStatus = value;
170ded66d0fSJason Ling break;
171ded66d0fSJason Ling case UpdateState::verificationCompleted:
172ded66d0fSJason Ling value = lastVerificationStatus;
173ded66d0fSJason Ling break;
174ded66d0fSJason Ling case UpdateState::updatePending:
175ded66d0fSJason Ling value = ActionStatus::unknown;
176ded66d0fSJason Ling break;
177ded66d0fSJason Ling case UpdateState::updateStarted:
178ded66d0fSJason Ling if (!pack)
179ded66d0fSJason Ling {
180ded66d0fSJason Ling break;
181ded66d0fSJason Ling }
182ded66d0fSJason Ling value = pack->update->status();
183ded66d0fSJason Ling lastUpdateStatus = value;
184ded66d0fSJason Ling break;
185ded66d0fSJason Ling case UpdateState::updateCompleted:
186ded66d0fSJason Ling value = lastUpdateStatus;
187ded66d0fSJason Ling break;
188ded66d0fSJason Ling default:
189ded66d0fSJason Ling break;
190ded66d0fSJason Ling }
191ded66d0fSJason Ling
192ded66d0fSJason Ling return value;
193ded66d0fSJason Ling }
194ded66d0fSJason Ling
195ded66d0fSJason Ling /*
196ded66d0fSJason Ling * Return stat information on an open session. It therefore must be an active
197ded66d0fSJason Ling * handle to either the active image or active hash.
198ded66d0fSJason Ling */
stat(uint16_t session,blobs::BlobMeta * meta)199ded66d0fSJason Ling bool FirmwareBlobHandler::stat(uint16_t session, blobs::BlobMeta* meta)
200ded66d0fSJason Ling {
201ded66d0fSJason Ling auto item = lookup.find(session);
202ded66d0fSJason Ling if (item == lookup.end())
203ded66d0fSJason Ling {
204ded66d0fSJason Ling return false;
205ded66d0fSJason Ling }
206ded66d0fSJason Ling
207166b4f19SManojkiran Eda /* The size here refers to the size of the file -- of something analogous.
208ded66d0fSJason Ling */
209ded66d0fSJason Ling meta->size = (item->second->imageHandler)
210ded66d0fSJason Ling ? item->second->imageHandler->getSize()
211ded66d0fSJason Ling : 0;
212ded66d0fSJason Ling
213ded66d0fSJason Ling meta->metadata.clear();
214ded66d0fSJason Ling
215ded66d0fSJason Ling if (item->second->activePath == verifyBlobId ||
216ded66d0fSJason Ling item->second->activePath == updateBlobId)
217ded66d0fSJason Ling {
218ded66d0fSJason Ling ActionStatus value = getActionStatus();
219ded66d0fSJason Ling
220ded66d0fSJason Ling meta->metadata.push_back(static_cast<std::uint8_t>(value));
221ded66d0fSJason Ling
222ded66d0fSJason Ling /* Change the firmware handler's state and the blob's stat value
223ded66d0fSJason Ling * depending.
224ded66d0fSJason Ling */
225ded66d0fSJason Ling if (value == ActionStatus::success || value == ActionStatus::failed)
226ded66d0fSJason Ling {
227ded66d0fSJason Ling if (item->second->activePath == verifyBlobId)
228ded66d0fSJason Ling {
229ded66d0fSJason Ling changeState(UpdateState::verificationCompleted);
230ded66d0fSJason Ling }
231ded66d0fSJason Ling else
232ded66d0fSJason Ling {
233ded66d0fSJason Ling /* item->second->activePath == updateBlobId */
234ded66d0fSJason Ling changeState(UpdateState::updateCompleted);
235ded66d0fSJason Ling }
236ded66d0fSJason Ling
237ded66d0fSJason Ling item->second->flags &= ~blobs::StateFlags::committing;
238ded66d0fSJason Ling
239ded66d0fSJason Ling if (value == ActionStatus::success)
240ded66d0fSJason Ling {
241ded66d0fSJason Ling item->second->flags |= blobs::StateFlags::committed;
242ded66d0fSJason Ling }
243ded66d0fSJason Ling else
244ded66d0fSJason Ling {
245ded66d0fSJason Ling item->second->flags |= blobs::StateFlags::commit_error;
246ded66d0fSJason Ling }
247ded66d0fSJason Ling }
248ded66d0fSJason Ling }
249ded66d0fSJason Ling
250166b4f19SManojkiran Eda /* The blobState here relates to an active session, so we should return the
251ded66d0fSJason Ling * flags used to open this session.
252ded66d0fSJason Ling */
253ded66d0fSJason Ling meta->blobState = item->second->flags;
254ded66d0fSJason Ling
255ded66d0fSJason Ling /* The metadata blob returned comes from the data handler... it's used for
256ded66d0fSJason Ling * instance, in P2A bridging to get required information about the mapping,
257ded66d0fSJason Ling * and is the "opposite" of the lpc writemeta requirement.
258ded66d0fSJason Ling */
259ded66d0fSJason Ling if (item->second->dataHandler)
260ded66d0fSJason Ling {
261ded66d0fSJason Ling auto bytes = item->second->dataHandler->readMeta();
262ded66d0fSJason Ling meta->metadata.insert(meta->metadata.begin(), bytes.begin(),
263ded66d0fSJason Ling bytes.end());
264ded66d0fSJason Ling }
265ded66d0fSJason Ling
266ded66d0fSJason Ling return true;
267ded66d0fSJason Ling }
268ded66d0fSJason Ling
269ded66d0fSJason Ling /*
270ded66d0fSJason Ling * If you open /flash/image or /flash/tarball, or /flash/hash it will
271ded66d0fSJason Ling * interpret the open flags and perform whatever actions are required for
272ded66d0fSJason Ling * that update process. The session returned can be used immediately for
273ded66d0fSJason Ling * sending data down, without requiring one to open the new active file.
274ded66d0fSJason Ling *
275ded66d0fSJason Ling * If you open the active flash image or active hash it will let you
276ded66d0fSJason Ling * overwrite pieces, depending on the state.
277ded66d0fSJason Ling *
278ded66d0fSJason Ling * Once the verification process has started the active files cannot be
279ded66d0fSJason Ling * opened.
280ded66d0fSJason Ling *
281ded66d0fSJason Ling * You can only have one open session at a time. Which means, you can only
282ded66d0fSJason Ling * have one file open at a time. Trying to open the hash blob_id while you
283ded66d0fSJason Ling * still have the flash image blob_id open will fail. Opening the flash
284ded66d0fSJason Ling * blob_id when it is already open will fail.
285ded66d0fSJason Ling */
open(uint16_t session,uint16_t flags,const std::string & path)286ded66d0fSJason Ling bool FirmwareBlobHandler::open(uint16_t session, uint16_t flags,
287ded66d0fSJason Ling const std::string& path)
288ded66d0fSJason Ling {
289ded66d0fSJason Ling /* Is there an open session already? We only allow one at a time.
290ded66d0fSJason Ling *
291ded66d0fSJason Ling * Further on this, if there's an active session to the hash we don't allow
292ded66d0fSJason Ling * re-opening the image, and if we have the image open, we don't allow
293ded66d0fSJason Ling * opening the hash. This design decision may be re-evaluated, and changed
294ded66d0fSJason Ling * to only allow one session per object type (of the two types). But,
295ded66d0fSJason Ling * consider if the hash is open, do we want to allow writing to the image?
296ded66d0fSJason Ling * And why would we? But, really, the point of no-return is once the
297ded66d0fSJason Ling * verification process has begun -- which is done via commit() on the hash
298ded66d0fSJason Ling * blob_id, we no longer want to allow updating the contents.
299ded66d0fSJason Ling */
300ded66d0fSJason Ling if (fileOpen())
301ded66d0fSJason Ling {
302ded66d0fSJason Ling return false;
303ded66d0fSJason Ling }
304ded66d0fSJason Ling
305ded66d0fSJason Ling /* The active blobs are only meant to indicate status that something has
306ded66d0fSJason Ling * opened the image file or the hash file.
307ded66d0fSJason Ling */
308ded66d0fSJason Ling if (path == activeImageBlobId || path == activeHashBlobId)
309ded66d0fSJason Ling {
310ded66d0fSJason Ling /* 2a) are they opening the active image? this can only happen if they
311ded66d0fSJason Ling * already started one (due to canHandleBlob's behavior).
312ded66d0fSJason Ling */
313ded66d0fSJason Ling /* 2b) are they opening the active hash? this can only happen if they
314ded66d0fSJason Ling * already started one (due to canHandleBlob's behavior).
315ded66d0fSJason Ling */
316ded66d0fSJason Ling return false;
317ded66d0fSJason Ling }
318ded66d0fSJason Ling
319ded66d0fSJason Ling /* Check that they've opened for writing - read back not currently
320ded66d0fSJason Ling * supported.
321ded66d0fSJason Ling */
322ded66d0fSJason Ling if ((flags & blobs::OpenFlags::write) == 0)
323ded66d0fSJason Ling {
324ded66d0fSJason Ling return false;
325ded66d0fSJason Ling }
326ded66d0fSJason Ling
327ded66d0fSJason Ling /* Because canHandleBlob is called before open, we know that if they try to
328ded66d0fSJason Ling * open the verifyBlobId, they're in a state where it's present.
329ded66d0fSJason Ling */
330ded66d0fSJason Ling
331ded66d0fSJason Ling switch (state)
332ded66d0fSJason Ling {
333ded66d0fSJason Ling case UpdateState::notYetStarted:
334ded66d0fSJason Ling /* Only hashBlobId and firmware BlobIds present. */
335ded66d0fSJason Ling break;
336ded66d0fSJason Ling case UpdateState::uploadInProgress:
337ded66d0fSJason Ling /* Unreachable code because if it's started a file is open. */
338ded66d0fSJason Ling break;
339ded66d0fSJason Ling case UpdateState::verificationPending:
340ded66d0fSJason Ling /* Handle opening the verifyBlobId --> we know the image and hash
341ded66d0fSJason Ling * aren't open because of the fileOpen() check. They can still open
342ded66d0fSJason Ling * other files from this state to transition back into
343ded66d0fSJason Ling * uploadInProgress.
344ded66d0fSJason Ling *
345ded66d0fSJason Ling * The file must be opened for writing, but no transport mechanism
346ded66d0fSJason Ling * specified since it's irrelevant.
347ded66d0fSJason Ling */
348ded66d0fSJason Ling if (path == verifyBlobId)
349ded66d0fSJason Ling {
350ded66d0fSJason Ling verifyImage.flags = flags;
351ded66d0fSJason Ling
352ded66d0fSJason Ling lookup[session] = &verifyImage;
353ded66d0fSJason Ling
354ded66d0fSJason Ling return true;
355ded66d0fSJason Ling }
356ded66d0fSJason Ling break;
357ded66d0fSJason Ling case UpdateState::verificationStarted:
358ded66d0fSJason Ling case UpdateState::verificationCompleted:
359ded66d0fSJason Ling /* Unreachable code because if it's started a file is open. */
360ded66d0fSJason Ling return false;
361ded66d0fSJason Ling case UpdateState::updatePending:
362ded66d0fSJason Ling {
363ded66d0fSJason Ling /* When in this state, they can only open the updateBlobId */
364ded66d0fSJason Ling if (path == updateBlobId)
365ded66d0fSJason Ling {
366ded66d0fSJason Ling updateImage.flags = flags;
367ded66d0fSJason Ling
368ded66d0fSJason Ling lookup[session] = &updateImage;
369ded66d0fSJason Ling
370ded66d0fSJason Ling return true;
371ded66d0fSJason Ling }
372ded66d0fSJason Ling else
373ded66d0fSJason Ling {
374ded66d0fSJason Ling return false;
375ded66d0fSJason Ling }
376ded66d0fSJason Ling }
377ded66d0fSJason Ling case UpdateState::updateStarted:
378ded66d0fSJason Ling case UpdateState::updateCompleted:
379ded66d0fSJason Ling /* Unreachable code because if it's started a file is open. */
380ded66d0fSJason Ling break;
381ded66d0fSJason Ling default:
382ded66d0fSJason Ling break;
383ded66d0fSJason Ling }
384ded66d0fSJason Ling
385ded66d0fSJason Ling /* To support multiple firmware options, we need to make sure they're
386ded66d0fSJason Ling * opening the one they already opened during this update sequence, or it's
387ded66d0fSJason Ling * the first time they're opening it.
388ded66d0fSJason Ling */
389ded66d0fSJason Ling if (path != hashBlobId)
390ded66d0fSJason Ling {
391ded66d0fSJason Ling /* If they're not opening the hashBlobId they must be opening a firmware
392ded66d0fSJason Ling * handler.
393ded66d0fSJason Ling */
394ded66d0fSJason Ling if (openedFirmwareType.empty())
395ded66d0fSJason Ling {
396ded66d0fSJason Ling /* First time for this sequence. */
397ded66d0fSJason Ling openedFirmwareType = path;
398ded66d0fSJason Ling }
399ded66d0fSJason Ling else
400ded66d0fSJason Ling {
401ded66d0fSJason Ling if (openedFirmwareType != path)
402ded66d0fSJason Ling {
403ded66d0fSJason Ling /* Previously, in this sequence they opened /flash/image, and
404ded66d0fSJason Ling * now they're opening /flash/bios without finishing out
405ded66d0fSJason Ling * /flash/image (for example).
406ded66d0fSJason Ling */
407ded66d0fSJason Ling std::fprintf(stderr, "Trying to open alternate firmware while "
408ded66d0fSJason Ling "unfinished with other firmware.\n");
409ded66d0fSJason Ling return false;
410ded66d0fSJason Ling }
411ded66d0fSJason Ling }
412ded66d0fSJason Ling }
413ded66d0fSJason Ling
414ded66d0fSJason Ling /* There are two abstractions at play, how you get the data and how you
415ded66d0fSJason Ling * handle that data. such that, whether the data comes from the PCI bridge
416ded66d0fSJason Ling * or LPC bridge is not connected to whether the data goes into a static
417ded66d0fSJason Ling * layout flash update or a UBI tarball.
418ded66d0fSJason Ling */
419ded66d0fSJason Ling
420ded66d0fSJason Ling std::uint16_t transportFlag = flags & transportMask;
421ded66d0fSJason Ling
422ded66d0fSJason Ling /* How are they expecting to copy this data? */
423ded66d0fSJason Ling auto d = std::find_if(transports.begin(), transports.end(),
424ded66d0fSJason Ling [&transportFlag](const auto& iter) {
425ded66d0fSJason Ling return (iter.bitmask == transportFlag);
426ded66d0fSJason Ling });
427ded66d0fSJason Ling if (d == transports.end())
428ded66d0fSJason Ling {
429ded66d0fSJason Ling return false;
430ded66d0fSJason Ling }
431ded66d0fSJason Ling
432ded66d0fSJason Ling /* We found the transport handler they requested */
433ded66d0fSJason Ling
434ded66d0fSJason Ling /* Elsewhere I do this check by checking "if ::ipmi" because that's the
435ded66d0fSJason Ling * only non-external data pathway -- but this is just a more generic
436ded66d0fSJason Ling * approach to that.
437ded66d0fSJason Ling */
438ded66d0fSJason Ling if (d->handler)
439ded66d0fSJason Ling {
440ded66d0fSJason Ling /* If the data handler open call fails, open fails. */
441ded66d0fSJason Ling if (!d->handler->open())
442ded66d0fSJason Ling {
443ded66d0fSJason Ling return false;
444ded66d0fSJason Ling }
445ded66d0fSJason Ling }
446ded66d0fSJason Ling
447ded66d0fSJason Ling /* Do we have a file handler for the type of file they're opening.
448ded66d0fSJason Ling * Note: This should only fail if something is somehow crazy wrong.
449ded66d0fSJason Ling * Since the canHandle() said yes, and that's tied into the list of explicit
450ded66d0fSJason Ling * firmware handers (and file handlers, like this'll know where to write the
451ded66d0fSJason Ling * tarball, etc).
452ded66d0fSJason Ling */
453a9423469SPatrick Williams auto h = std::find_if(
454a9423469SPatrick Williams handlers.begin(), handlers.end(),
455a9423469SPatrick Williams [&path](const auto& iter) { return (iter.blobName == path); });
456ded66d0fSJason Ling if (h == handlers.end())
457ded66d0fSJason Ling {
458ded66d0fSJason Ling return false;
459ded66d0fSJason Ling }
460ded66d0fSJason Ling
461ded66d0fSJason Ling /* Ok, so we found a handler that matched, so call open() */
46256a2273fSJason Ling if (!h->handler->open(path, std::ios::out))
463ded66d0fSJason Ling {
464ded66d0fSJason Ling return false;
465ded66d0fSJason Ling }
466ded66d0fSJason Ling
467ded66d0fSJason Ling Session* curr;
468ded66d0fSJason Ling const char* active;
469ded66d0fSJason Ling
470ded66d0fSJason Ling if (path == hashBlobId)
471ded66d0fSJason Ling {
472ded66d0fSJason Ling /* 2c) are they opening the /flash/hash ? (to start the process) */
473ded66d0fSJason Ling curr = &activeHash;
474ded66d0fSJason Ling active = activeHashBlobId;
475ded66d0fSJason Ling }
476ded66d0fSJason Ling else
477ded66d0fSJason Ling {
478ded66d0fSJason Ling curr = &activeImage;
479ded66d0fSJason Ling active = activeImageBlobId;
480ded66d0fSJason Ling }
481ded66d0fSJason Ling
482ded66d0fSJason Ling curr->flags = flags;
483ded66d0fSJason Ling curr->dataHandler = d->handler.get();
484ded66d0fSJason Ling curr->imageHandler = h->handler.get();
485ded66d0fSJason Ling
486ded66d0fSJason Ling lookup[session] = curr;
487ded66d0fSJason Ling
488ded66d0fSJason Ling addBlobId(active);
489ded66d0fSJason Ling removeBlobId(verifyBlobId);
490ded66d0fSJason Ling
491ded66d0fSJason Ling changeState(UpdateState::uploadInProgress);
492ded66d0fSJason Ling
493ded66d0fSJason Ling return true;
494ded66d0fSJason Ling }
495ded66d0fSJason Ling
496ded66d0fSJason Ling /**
497ded66d0fSJason Ling * The write command really just grabs the data from wherever it is and sends it
498ded66d0fSJason Ling * to the image handler. It's the image handler's responsibility to deal with
499ded66d0fSJason Ling * the data provided.
500ded66d0fSJason Ling *
501ded66d0fSJason Ling * This receives a session from the blob manager, therefore it is always called
502ded66d0fSJason Ling * between open() and close().
503ded66d0fSJason Ling */
write(uint16_t session,uint32_t offset,const std::vector<uint8_t> & data)504ded66d0fSJason Ling bool FirmwareBlobHandler::write(uint16_t session, uint32_t offset,
505ded66d0fSJason Ling const std::vector<uint8_t>& data)
506ded66d0fSJason Ling {
507ded66d0fSJason Ling auto item = lookup.find(session);
508ded66d0fSJason Ling if (item == lookup.end())
509ded66d0fSJason Ling {
510ded66d0fSJason Ling return false;
511ded66d0fSJason Ling }
512ded66d0fSJason Ling
513ded66d0fSJason Ling /* Prevent writing during verification. */
514ded66d0fSJason Ling if (state == UpdateState::verificationStarted)
515ded66d0fSJason Ling {
516ded66d0fSJason Ling return false;
517ded66d0fSJason Ling }
518ded66d0fSJason Ling
519ded66d0fSJason Ling /* Prevent writing to the verification or update blobs. */
520ded66d0fSJason Ling if (item->second->activePath == verifyBlobId ||
521ded66d0fSJason Ling item->second->activePath == updateBlobId)
522ded66d0fSJason Ling {
523ded66d0fSJason Ling return false;
524ded66d0fSJason Ling }
525ded66d0fSJason Ling
526ded66d0fSJason Ling std::vector<std::uint8_t> bytes;
527ded66d0fSJason Ling
528ded66d0fSJason Ling if (item->second->flags & FirmwareFlags::UpdateFlags::ipmi)
529ded66d0fSJason Ling {
530ded66d0fSJason Ling bytes = data;
531ded66d0fSJason Ling }
532ded66d0fSJason Ling else
533ded66d0fSJason Ling {
534ded66d0fSJason Ling /* little endian required per design, and so on, but TODO: do endianness
535ded66d0fSJason Ling * with boost.
536ded66d0fSJason Ling */
537ded66d0fSJason Ling struct ExtChunkHdr header;
538ded66d0fSJason Ling
539ded66d0fSJason Ling if (data.size() != sizeof(header))
540ded66d0fSJason Ling {
541ded66d0fSJason Ling return false;
542ded66d0fSJason Ling }
543ded66d0fSJason Ling
544ded66d0fSJason Ling std::memcpy(&header, data.data(), data.size());
545ded66d0fSJason Ling bytes = item->second->dataHandler->copyFrom(header.length);
546ded66d0fSJason Ling }
547ded66d0fSJason Ling
548ded66d0fSJason Ling return item->second->imageHandler->write(offset, bytes);
549ded66d0fSJason Ling }
550ded66d0fSJason Ling
551ded66d0fSJason Ling /*
552ded66d0fSJason Ling * If the active session (image or hash) is over LPC, this allows
553ded66d0fSJason Ling * configuring it. This option is only available before you start
554ded66d0fSJason Ling * writing data for the given item (image or hash). This will return
555ded66d0fSJason Ling * false at any other part. -- the lpc handler portion will know to return
556ded66d0fSJason Ling * false.
557ded66d0fSJason Ling */
writeMeta(uint16_t session,uint32_t,const std::vector<uint8_t> & data)558b487eb47SWilly Tu bool FirmwareBlobHandler::writeMeta(uint16_t session, uint32_t,
559ded66d0fSJason Ling const std::vector<uint8_t>& data)
560ded66d0fSJason Ling {
561ded66d0fSJason Ling auto item = lookup.find(session);
562ded66d0fSJason Ling if (item == lookup.end())
563ded66d0fSJason Ling {
564ded66d0fSJason Ling return false;
565ded66d0fSJason Ling }
566ded66d0fSJason Ling
567ded66d0fSJason Ling if (item->second->flags & FirmwareFlags::UpdateFlags::ipmi)
568ded66d0fSJason Ling {
569ded66d0fSJason Ling return false;
570ded66d0fSJason Ling }
571ded66d0fSJason Ling
572ded66d0fSJason Ling /* Prevent writing meta to the verification blob (it has no data handler).
573ded66d0fSJason Ling */
574ded66d0fSJason Ling if (item->second->dataHandler)
575ded66d0fSJason Ling {
576ded66d0fSJason Ling return item->second->dataHandler->writeMeta(data);
577ded66d0fSJason Ling }
578ded66d0fSJason Ling
579ded66d0fSJason Ling return false;
580ded66d0fSJason Ling }
581ded66d0fSJason Ling
582ded66d0fSJason Ling /*
583ded66d0fSJason Ling * If this command is called on the session for the verifyBlobId, it'll
584ded66d0fSJason Ling * trigger a systemd service `verify_image.service` to attempt to verify
585ded66d0fSJason Ling * the image.
586ded66d0fSJason Ling *
587ded66d0fSJason Ling * For this file to have opened, the other two must be closed, which means any
588ded66d0fSJason Ling * out-of-band transport mechanism involved is closed.
589ded66d0fSJason Ling */
commit(uint16_t session,const std::vector<uint8_t> &)590b487eb47SWilly Tu bool FirmwareBlobHandler::commit(uint16_t session, const std::vector<uint8_t>&)
591ded66d0fSJason Ling {
592ded66d0fSJason Ling auto item = lookup.find(session);
593ded66d0fSJason Ling if (item == lookup.end())
594ded66d0fSJason Ling {
595ded66d0fSJason Ling return false;
596ded66d0fSJason Ling }
597ded66d0fSJason Ling
598ded66d0fSJason Ling /* You can only commit on the verifyBlodId or updateBlobId */
599ded66d0fSJason Ling if (item->second->activePath != verifyBlobId &&
600ded66d0fSJason Ling item->second->activePath != updateBlobId)
601ded66d0fSJason Ling {
602ded66d0fSJason Ling std::fprintf(stderr, "path: '%s' not expected for commit\n",
603ded66d0fSJason Ling item->second->activePath.c_str());
604ded66d0fSJason Ling return false;
605ded66d0fSJason Ling }
606ded66d0fSJason Ling
607ded66d0fSJason Ling switch (state)
608ded66d0fSJason Ling {
609ded66d0fSJason Ling case UpdateState::verificationPending:
610ded66d0fSJason Ling /* Set state to committing. */
611ded66d0fSJason Ling item->second->flags |= blobs::StateFlags::committing;
612ded66d0fSJason Ling return triggerVerification();
613ded66d0fSJason Ling case UpdateState::verificationStarted:
614ded66d0fSJason Ling /* Calling repeatedly has no effect within an update process. */
615ded66d0fSJason Ling return true;
616ded66d0fSJason Ling case UpdateState::verificationCompleted:
617ded66d0fSJason Ling /* Calling after the verification process has completed returns
618ded66d0fSJason Ling * failure. */
619ded66d0fSJason Ling return false;
620ded66d0fSJason Ling case UpdateState::updatePending:
621ded66d0fSJason Ling item->second->flags |= blobs::StateFlags::committing;
622ded66d0fSJason Ling return triggerUpdate();
623ded66d0fSJason Ling case UpdateState::updateStarted:
624ded66d0fSJason Ling /* Calling repeatedly has no effect within an update process. */
625ded66d0fSJason Ling return true;
626ded66d0fSJason Ling default:
627ded66d0fSJason Ling return false;
628ded66d0fSJason Ling }
629ded66d0fSJason Ling }
630ded66d0fSJason Ling
631ded66d0fSJason Ling /*
632ded66d0fSJason Ling * Close must be called on the firmware image before triggering
633ded66d0fSJason Ling * verification via commit. Once the verification is complete, you can
634ded66d0fSJason Ling * then close the hash file.
635ded66d0fSJason Ling *
636ded66d0fSJason Ling * If the `verify_image.service` returned success, closing the hash file
637ded66d0fSJason Ling * will have a specific behavior depending on the update. If it's UBI,
638ded66d0fSJason Ling * it'll perform the install. If it's static layout, it'll do nothing. The
639ded66d0fSJason Ling * verify_image service in the static layout case is responsible for placing
640ded66d0fSJason Ling * the file in the correct staging position.
641ded66d0fSJason Ling */
close(uint16_t session)642ded66d0fSJason Ling bool FirmwareBlobHandler::close(uint16_t session)
643ded66d0fSJason Ling {
644ded66d0fSJason Ling auto item = lookup.find(session);
645ded66d0fSJason Ling if (item == lookup.end())
646ded66d0fSJason Ling {
647ded66d0fSJason Ling return false;
648ded66d0fSJason Ling }
649ded66d0fSJason Ling
650ded66d0fSJason Ling switch (state)
651ded66d0fSJason Ling {
652ded66d0fSJason Ling case UpdateState::uploadInProgress:
653ded66d0fSJason Ling /* They are closing a data pathway (image, tarball, hash). */
654ded66d0fSJason Ling changeState(UpdateState::verificationPending);
655ded66d0fSJason Ling
656166b4f19SManojkiran Eda /* Add verify blob ID now that we can expect it, IF they also wrote
657ded66d0fSJason Ling * some data.
658ded66d0fSJason Ling */
659ded66d0fSJason Ling if (std::count(blobIDs.begin(), blobIDs.end(), activeImageBlobId))
660ded66d0fSJason Ling {
661ded66d0fSJason Ling addBlobId(verifyBlobId);
662ded66d0fSJason Ling }
663ded66d0fSJason Ling break;
664ded66d0fSJason Ling case UpdateState::verificationPending:
665ded66d0fSJason Ling /* They haven't triggered, therefore closing is uninteresting.
666ded66d0fSJason Ling */
667ded66d0fSJason Ling break;
668ded66d0fSJason Ling case UpdateState::verificationStarted:
669ded66d0fSJason Ling /* Abort without checking to see if it happened to finish. Require
670ded66d0fSJason Ling * the caller to stat() deliberately.
671ded66d0fSJason Ling */
672ded66d0fSJason Ling abortVerification();
673ded66d0fSJason Ling abortProcess();
674ded66d0fSJason Ling break;
675ded66d0fSJason Ling case UpdateState::verificationCompleted:
676ded66d0fSJason Ling if (lastVerificationStatus == ActionStatus::success)
677ded66d0fSJason Ling {
678ded66d0fSJason Ling changeState(UpdateState::updatePending);
679ded66d0fSJason Ling addBlobId(updateBlobId);
680ded66d0fSJason Ling removeBlobId(verifyBlobId);
681ded66d0fSJason Ling }
682ded66d0fSJason Ling else
683ded66d0fSJason Ling {
684ded66d0fSJason Ling /* Verification failed, and the host-tool knows this by calling
685ded66d0fSJason Ling * stat(), which triggered the state change to
686ded66d0fSJason Ling * verificationCompleted.
687ded66d0fSJason Ling *
688ded66d0fSJason Ling * Therefore, let's abort the process at this point.
689ded66d0fSJason Ling */
690ded66d0fSJason Ling abortProcess();
691ded66d0fSJason Ling }
692ded66d0fSJason Ling break;
693ded66d0fSJason Ling case UpdateState::updatePending:
694ded66d0fSJason Ling /* They haven't triggered the update, therefore this is
695ded66d0fSJason Ling * uninteresting. */
696ded66d0fSJason Ling break;
697ded66d0fSJason Ling case UpdateState::updateStarted:
698ded66d0fSJason Ling /* Abort without checking to see if it happened to finish. Require
699ded66d0fSJason Ling * the caller to stat() deliberately.
700ded66d0fSJason Ling */
701ded66d0fSJason Ling abortUpdate();
702ded66d0fSJason Ling abortProcess();
703ded66d0fSJason Ling break;
704ded66d0fSJason Ling case UpdateState::updateCompleted:
705ded66d0fSJason Ling if (lastUpdateStatus == ActionStatus::failed)
706ded66d0fSJason Ling {
707ded66d0fSJason Ling /* TODO: lOG something? */
708ded66d0fSJason Ling std::fprintf(stderr, "Update failed\n");
709ded66d0fSJason Ling }
710ded66d0fSJason Ling
711ded66d0fSJason Ling abortProcess();
712ded66d0fSJason Ling break;
713ded66d0fSJason Ling default:
714ded66d0fSJason Ling break;
715ded66d0fSJason Ling }
716ded66d0fSJason Ling
717ded66d0fSJason Ling if (!lookup.empty())
718ded66d0fSJason Ling {
719ded66d0fSJason Ling if (item->second->dataHandler)
720ded66d0fSJason Ling {
721ded66d0fSJason Ling item->second->dataHandler->close();
722ded66d0fSJason Ling }
723ded66d0fSJason Ling if (item->second->imageHandler)
724ded66d0fSJason Ling {
725ded66d0fSJason Ling item->second->imageHandler->close();
726ded66d0fSJason Ling }
727ded66d0fSJason Ling lookup.erase(item);
728ded66d0fSJason Ling }
729ded66d0fSJason Ling return true;
730ded66d0fSJason Ling }
731ded66d0fSJason Ling
changeState(UpdateState next)732ded66d0fSJason Ling void FirmwareBlobHandler::changeState(UpdateState next)
733ded66d0fSJason Ling {
734ded66d0fSJason Ling state = next;
735ded66d0fSJason Ling
736ded66d0fSJason Ling if (state == UpdateState::notYetStarted)
737ded66d0fSJason Ling {
738ded66d0fSJason Ling /* Going back to notyetstarted, let them trigger preparation again. */
739ded66d0fSJason Ling preparationTriggered = false;
740ded66d0fSJason Ling }
741ded66d0fSJason Ling else if (state == UpdateState::uploadInProgress)
742ded66d0fSJason Ling {
743ded66d0fSJason Ling /* Store this transition logic here instead of ::open() */
744ded66d0fSJason Ling if (!preparationTriggered)
745ded66d0fSJason Ling {
746ded66d0fSJason Ling auto* pack = getActionPack();
747ded66d0fSJason Ling if (pack)
748ded66d0fSJason Ling {
749ded66d0fSJason Ling pack->preparation->trigger();
750ded66d0fSJason Ling preparationTriggered = true;
751ded66d0fSJason Ling }
752ded66d0fSJason Ling }
753ded66d0fSJason Ling }
754ded66d0fSJason Ling }
755ded66d0fSJason Ling
expire(uint16_t)756b487eb47SWilly Tu bool FirmwareBlobHandler::expire(uint16_t)
757ded66d0fSJason Ling {
758ded66d0fSJason Ling abortProcess();
759ded66d0fSJason Ling return true;
760ded66d0fSJason Ling }
761ded66d0fSJason Ling
762ded66d0fSJason Ling /*
763ded66d0fSJason Ling * Currently, the design does not provide this with a function, however,
764ded66d0fSJason Ling * it will likely change to support reading data back.
765ded66d0fSJason Ling */
read(uint16_t,uint32_t,uint32_t)766b487eb47SWilly Tu std::vector<uint8_t> FirmwareBlobHandler::read(uint16_t, uint32_t, uint32_t)
767ded66d0fSJason Ling {
768ded66d0fSJason Ling return {};
769ded66d0fSJason Ling }
770ded66d0fSJason Ling
abortProcess()771ded66d0fSJason Ling void FirmwareBlobHandler::abortProcess()
772ded66d0fSJason Ling {
773ded66d0fSJason Ling /* Closing of open files is handled from close() -- Reaching here from
774ded66d0fSJason Ling * delete may never be supported.
775ded66d0fSJason Ling */
776ded66d0fSJason Ling removeBlobId(verifyBlobId);
777ded66d0fSJason Ling removeBlobId(updateBlobId);
778ded66d0fSJason Ling removeBlobId(activeImageBlobId);
779ded66d0fSJason Ling removeBlobId(activeHashBlobId);
780ded66d0fSJason Ling
781ded66d0fSJason Ling for (auto item : lookup)
782ded66d0fSJason Ling {
783ded66d0fSJason Ling if (item.second->dataHandler)
784ded66d0fSJason Ling {
785ded66d0fSJason Ling item.second->dataHandler->close();
786ded66d0fSJason Ling }
787ded66d0fSJason Ling if (item.second->imageHandler)
788ded66d0fSJason Ling {
789ded66d0fSJason Ling item.second->imageHandler->close();
790ded66d0fSJason Ling }
791ded66d0fSJason Ling }
792ded66d0fSJason Ling lookup.clear();
793ded66d0fSJason Ling
794ded66d0fSJason Ling openedFirmwareType = "";
795ded66d0fSJason Ling changeState(UpdateState::notYetStarted);
796ded66d0fSJason Ling }
797ded66d0fSJason Ling
abortVerification()798ded66d0fSJason Ling void FirmwareBlobHandler::abortVerification()
799ded66d0fSJason Ling {
800ded66d0fSJason Ling auto* pack = getActionPack();
801ded66d0fSJason Ling if (pack)
802ded66d0fSJason Ling {
803ded66d0fSJason Ling pack->verification->abort();
804ded66d0fSJason Ling }
805ded66d0fSJason Ling }
806ded66d0fSJason Ling
triggerVerification()807ded66d0fSJason Ling bool FirmwareBlobHandler::triggerVerification()
808ded66d0fSJason Ling {
809ded66d0fSJason Ling auto* pack = getActionPack();
810ded66d0fSJason Ling if (!pack)
811ded66d0fSJason Ling {
812ded66d0fSJason Ling return false;
813ded66d0fSJason Ling }
814ded66d0fSJason Ling
815ded66d0fSJason Ling bool result = pack->verification->trigger();
816ded66d0fSJason Ling if (result)
817ded66d0fSJason Ling {
818ded66d0fSJason Ling changeState(UpdateState::verificationStarted);
819ded66d0fSJason Ling }
820ded66d0fSJason Ling
821ded66d0fSJason Ling return result;
822ded66d0fSJason Ling }
823ded66d0fSJason Ling
abortUpdate()824ded66d0fSJason Ling void FirmwareBlobHandler::abortUpdate()
825ded66d0fSJason Ling {
826ded66d0fSJason Ling auto* pack = getActionPack();
827ded66d0fSJason Ling if (pack)
828ded66d0fSJason Ling {
829ded66d0fSJason Ling pack->update->abort();
830ded66d0fSJason Ling }
831ded66d0fSJason Ling }
832ded66d0fSJason Ling
triggerUpdate()833ded66d0fSJason Ling bool FirmwareBlobHandler::triggerUpdate()
834ded66d0fSJason Ling {
835ded66d0fSJason Ling auto* pack = getActionPack();
836ded66d0fSJason Ling if (!pack)
837ded66d0fSJason Ling {
838ded66d0fSJason Ling return false;
839ded66d0fSJason Ling }
840ded66d0fSJason Ling
841ded66d0fSJason Ling bool result = pack->update->trigger();
842ded66d0fSJason Ling if (result)
843ded66d0fSJason Ling {
844ded66d0fSJason Ling changeState(UpdateState::updateStarted);
845ded66d0fSJason Ling }
846ded66d0fSJason Ling
847ded66d0fSJason Ling return result;
848ded66d0fSJason Ling }
849ded66d0fSJason Ling
850ded66d0fSJason Ling } // namespace ipmi_flash
851