1bf58cd64SPatrick Venture /*
2bf58cd64SPatrick Venture  * Copyright 2018 Google Inc.
3bf58cd64SPatrick Venture  *
4bf58cd64SPatrick Venture  * Licensed under the Apache License, Version 2.0 (the "License");
5bf58cd64SPatrick Venture  * you may not use this file except in compliance with the License.
6bf58cd64SPatrick Venture  * You may obtain a copy of the License at
7bf58cd64SPatrick Venture  *
8bf58cd64SPatrick Venture  *     http://www.apache.org/licenses/LICENSE-2.0
9bf58cd64SPatrick Venture  *
10bf58cd64SPatrick Venture  * Unless required by applicable law or agreed to in writing, software
11bf58cd64SPatrick Venture  * distributed under the License is distributed on an "AS IS" BASIS,
12bf58cd64SPatrick Venture  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13bf58cd64SPatrick Venture  * See the License for the specific language governing permissions and
14bf58cd64SPatrick Venture  * limitations under the License.
15bf58cd64SPatrick Venture  */
16bf58cd64SPatrick Venture 
17bf58cd64SPatrick Venture #include "updater.hpp"
18bf58cd64SPatrick Venture 
192bc23fe1SPatrick Venture #include "tool_errors.hpp"
200533d0b0SPatrick Venture 
2100887597SPatrick Venture #include <algorithm>
22664c5bc7SPatrick Venture #include <blobs-ipmid/blobs.hpp>
23339dece8SPatrick Venture #include <cstring>
24664c5bc7SPatrick Venture #include <ipmiblob/blob_errors.hpp>
25af69625fSPatrick Venture #include <memory>
262a927e87SPatrick Venture #include <string>
27af69625fSPatrick Venture 
289b534f06SPatrick Venture namespace host_tool
299b534f06SPatrick Venture {
309b534f06SPatrick Venture 
31664c5bc7SPatrick Venture void updaterMain(ipmiblob::BlobInterface* blob, DataInterface* handler,
3200887597SPatrick Venture                  const std::string& imagePath, const std::string& signaturePath)
33bf58cd64SPatrick Venture {
34af69625fSPatrick Venture     /* TODO(venture): Add optional parameter to specify the flash type, default
35af69625fSPatrick Venture      * to legacy for now.
36*7dcca5ddSPatrick Venture      *
37*7dcca5ddSPatrick Venture      * TODO(venture): Move the strings from the FirmwareHandler object to a
38*7dcca5ddSPatrick Venture      * boring utils object so it will be more happly linked cleanly to both the
39*7dcca5ddSPatrick Venture      * BMC and host-side.
40af69625fSPatrick Venture      */
4100887597SPatrick Venture     std::string goalFirmware = "/flash/image";
4273528388SPatrick Venture     std::string hashFilename = "/flash/hash";
43*7dcca5ddSPatrick Venture     std::string verifyFilename = "/flash/verify";
4400887597SPatrick Venture 
450bf8bf0cSPatrick Venture     /* Get list of blob_ids, check for /flash/image, or /flash/tarball.
460bf8bf0cSPatrick Venture      * TODO(venture) the mechanism doesn't care, but the caller of burn_my_bmc
470bf8bf0cSPatrick Venture      * will have in mind which they're sending and we need to verify it's
480bf8bf0cSPatrick Venture      * available and use it.
490bf8bf0cSPatrick Venture      */
5000887597SPatrick Venture     std::vector<std::string> blobs = blob->getBlobList();
51339dece8SPatrick Venture     auto blobInst = std::find_if(
522a927e87SPatrick Venture         blobs.begin(), blobs.end(), [&goalFirmware](const std::string& iter) {
53339dece8SPatrick Venture             /* Running into weird scenarios where the string comparison doesn't
54339dece8SPatrick Venture              * work.  TODO: revisit.
55339dece8SPatrick Venture              */
56339dece8SPatrick Venture             return (0 == std::memcmp(goalFirmware.c_str(), iter.c_str(),
57339dece8SPatrick Venture                                      goalFirmware.length()));
58339dece8SPatrick Venture             // return (goalFirmware.compare(iter));
59339dece8SPatrick Venture         });
6000887597SPatrick Venture     if (blobInst == blobs.end())
6100887597SPatrick Venture     {
622bc23fe1SPatrick Venture         throw ToolException(goalFirmware + " not found");
6300887597SPatrick Venture     }
64af69625fSPatrick Venture 
65af69625fSPatrick Venture     /* Call stat on /flash/image (or /flash/tarball) and check if data interface
6600887597SPatrick Venture      * is supported.
6700887597SPatrick Venture      */
68664c5bc7SPatrick Venture     ipmiblob::StatResponse stat;
69339dece8SPatrick Venture     try
70339dece8SPatrick Venture     {
71339dece8SPatrick Venture         stat = blob->getStat(goalFirmware);
72339dece8SPatrick Venture     }
73664c5bc7SPatrick Venture     catch (const ipmiblob::BlobException& b)
74339dece8SPatrick Venture     {
75339dece8SPatrick Venture         throw ToolException("blob exception received: " +
76339dece8SPatrick Venture                             std::string(b.what()));
77339dece8SPatrick Venture     }
78339dece8SPatrick Venture 
79aa32a36aSPatrick Venture     auto supported = handler->supportedType();
80aa32a36aSPatrick Venture     if ((stat.blob_state & supported) == 0)
818a55dcbdSPatrick Venture     {
822bc23fe1SPatrick Venture         throw ToolException("data interface selected not supported.");
838a55dcbdSPatrick Venture     }
84af69625fSPatrick Venture 
850533d0b0SPatrick Venture     /* Yay, our data handler is supported. */
8673528388SPatrick Venture 
8773528388SPatrick Venture     /* Send over the firmware image. */
8873528388SPatrick Venture     std::fprintf(stderr, "Sending over the firmware image.\n");
890533d0b0SPatrick Venture     std::uint16_t session;
900533d0b0SPatrick Venture     try
910533d0b0SPatrick Venture     {
92664c5bc7SPatrick Venture         session = blob->openBlob(
93664c5bc7SPatrick Venture             goalFirmware,
94664c5bc7SPatrick Venture             static_cast<std::uint16_t>(supported) |
95664c5bc7SPatrick Venture                 static_cast<std::uint16_t>(blobs::OpenFlags::write));
960533d0b0SPatrick Venture     }
97664c5bc7SPatrick Venture     catch (const ipmiblob::BlobException& b)
980533d0b0SPatrick Venture     {
992bc23fe1SPatrick Venture         throw ToolException("blob exception received: " +
1002bc23fe1SPatrick Venture                             std::string(b.what()));
1010533d0b0SPatrick Venture     }
1020533d0b0SPatrick Venture 
103fd6aaec8SPatrick Venture     if (!handler->sendContents(imagePath, session))
104fd6aaec8SPatrick Venture     {
105f9566d88SPatrick Venture         /* Need to close the session on failure, or it's stuck open (until the
106f9566d88SPatrick Venture          * blob handler timeout is implemented, and even then, why make it wait.
107f9566d88SPatrick Venture          */
108f9566d88SPatrick Venture         blob->closeBlob(session);
1092bc23fe1SPatrick Venture         throw ToolException("Failed to send contents of " + imagePath);
110fd6aaec8SPatrick Venture     }
111fd6aaec8SPatrick Venture 
1129a5ce561SPatrick Venture     blob->closeBlob(session);
1139a5ce561SPatrick Venture 
114fd6aaec8SPatrick Venture     /* Send over the hash contents. */
11573528388SPatrick Venture     std::fprintf(stderr, "Sending over the hash file.\n");
11673528388SPatrick Venture     try
11773528388SPatrick Venture     {
11873528388SPatrick Venture         session = blob->openBlob(
11973528388SPatrick Venture             hashFilename,
12073528388SPatrick Venture             static_cast<std::uint16_t>(supported) |
12173528388SPatrick Venture                 static_cast<std::uint16_t>(blobs::OpenFlags::write));
12273528388SPatrick Venture     }
12373528388SPatrick Venture     catch (const ipmiblob::BlobException& b)
12473528388SPatrick Venture     {
12573528388SPatrick Venture         throw ToolException("blob exception received: " +
12673528388SPatrick Venture                             std::string(b.what()));
12773528388SPatrick Venture     }
12873528388SPatrick Venture 
12973528388SPatrick Venture     if (!handler->sendContents(signaturePath, session))
13073528388SPatrick Venture     {
13173528388SPatrick Venture         blob->closeBlob(session);
13273528388SPatrick Venture         throw ToolException("Failed to send contents of " + signaturePath);
13373528388SPatrick Venture     }
13473528388SPatrick Venture 
13573528388SPatrick Venture     blob->closeBlob(session);
13673528388SPatrick Venture 
137*7dcca5ddSPatrick Venture     /* Trigger the verification by opening the verify file. */
138*7dcca5ddSPatrick Venture     std::fprintf(stderr, "Opening the verification file\n");
139*7dcca5ddSPatrick Venture     try
140*7dcca5ddSPatrick Venture     {
141*7dcca5ddSPatrick Venture         session = blob->openBlob(
142*7dcca5ddSPatrick Venture             verifyFilename,
143*7dcca5ddSPatrick Venture             static_cast<std::uint16_t>(supported) |
144*7dcca5ddSPatrick Venture                 static_cast<std::uint16_t>(blobs::OpenFlags::write));
145*7dcca5ddSPatrick Venture     }
146*7dcca5ddSPatrick Venture     catch (const ipmiblob::BlobException& b)
147*7dcca5ddSPatrick Venture     {
148*7dcca5ddSPatrick Venture         throw ToolException("blob exception received: " +
149*7dcca5ddSPatrick Venture                             std::string(b.what()));
150*7dcca5ddSPatrick Venture     }
151*7dcca5ddSPatrick Venture 
152*7dcca5ddSPatrick Venture     std::fprintf(
153*7dcca5ddSPatrick Venture         stderr,
154*7dcca5ddSPatrick Venture         "Committing to verification file to trigger verification service\n");
155*7dcca5ddSPatrick Venture     try
156*7dcca5ddSPatrick Venture     {
157*7dcca5ddSPatrick Venture         blob->commit(session, {});
158*7dcca5ddSPatrick Venture     }
159*7dcca5ddSPatrick Venture     catch (const ipmiblob::BlobException& b)
160*7dcca5ddSPatrick Venture     {
161*7dcca5ddSPatrick Venture         throw ToolException("blob exception received: " +
162*7dcca5ddSPatrick Venture                             std::string(b.what()));
163*7dcca5ddSPatrick Venture     }
164*7dcca5ddSPatrick Venture 
165*7dcca5ddSPatrick Venture     /* TODO: Check the verification via stat(session). */
166*7dcca5ddSPatrick Venture 
167*7dcca5ddSPatrick Venture     /* DO NOT CLOSE the verification session until it's done. */
168*7dcca5ddSPatrick Venture     blob->closeBlob(session);
169fd6aaec8SPatrick Venture 
1702bc23fe1SPatrick Venture     return;
171bf58cd64SPatrick Venture }
1729b534f06SPatrick Venture 
1739b534f06SPatrick Venture } // namespace host_tool
174