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