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 "bt.hpp"
18 #include "io.hpp"
19 #include "lpc.hpp"
20 #include "p2a.hpp"
21 #include "pci.hpp"
22 #include "progress.hpp"
23 #include "tool_errors.hpp"
24 #include "updater.hpp"
25 
26 /* Use CLI11 argument parser once in openbmc/meta-oe or whatever. */
27 #include <getopt.h>
28 
29 #include <algorithm>
30 #include <cstdio>
31 #include <cstdlib>
32 #include <exception>
33 #include <iostream>
34 #include <ipmiblob/blob_handler.hpp>
35 #include <ipmiblob/ipmi_handler.hpp>
36 #include <iterator>
37 #include <limits>
38 #include <memory>
39 #include <string>
40 #include <vector>
41 
42 #define IPMILPC "ipmilpc"
43 #define IPMIPCI "ipmipci"
44 #define IPMIBT "ipmibt"
45 
46 namespace
47 {
48 const std::vector<std::string> interfaceList = {IPMIBT, IPMILPC, IPMIPCI};
49 } // namespace
50 
51 void usage(const char* program)
52 {
53     std::fprintf(
54         stderr,
55         "Usage: %s --command <command> --interface <interface> --image "
56         "<image file> --sig <signature file> --type <layout> "
57         "[--ignore-update]\n",
58         program);
59 
60     std::fprintf(stderr, "interfaces: ");
61     std::copy(interfaceList.begin(), interfaceList.end(),
62               std::ostream_iterator<std::string>(std::cerr, ", "));
63     std::fprintf(stderr, "\n");
64 
65     std::fprintf(stderr, "layouts examples: image, bios\n");
66     std::fprintf(stderr,
67                  "the type field specifies '/flash/{layout}' for a handler\n");
68 }
69 
70 bool checkCommand(const std::string& command)
71 {
72     return (command == "update");
73 }
74 
75 bool checkInterface(const std::string& interface)
76 {
77     auto intf =
78         std::find(interfaceList.begin(), interfaceList.end(), interface);
79     return (intf != interfaceList.end());
80 }
81 
82 int main(int argc, char* argv[])
83 {
84     std::string command, interface, imagePath, signaturePath, type;
85     char* valueEnd = nullptr;
86     long address = 0;
87     long length = 0;
88     std::uint32_t hostAddress = 0;
89     std::uint32_t hostLength = 0;
90     bool ignoreUpdate = false;
91 
92     while (1)
93     {
94         // clang-format off
95         static struct option long_options[] = {
96             {"command", required_argument, 0, 'c'},
97             {"interface", required_argument, 0, 'i'},
98             {"image", required_argument, 0, 'm'},
99             {"sig", required_argument, 0, 's'},
100             {"address", required_argument, 0, 'a'},
101             {"length", required_argument, 0, 'l'},
102             {"type", required_argument, 0, 't'},
103             {"ignore-update", no_argument, 0, 'u'},
104             {0, 0, 0, 0}
105         };
106         // clang-format on
107 
108         int option_index = 0;
109         int c = getopt_long(argc, argv, "c:i:m:s:a:l:t:u", long_options,
110                             &option_index);
111         if (c == -1)
112         {
113             break;
114         }
115 
116         switch (c)
117         {
118             case 'c':
119                 command = std::string{optarg};
120                 if (!checkCommand(command))
121                 {
122                     usage(argv[0]);
123                     exit(EXIT_FAILURE);
124                 }
125 
126                 break;
127             case 'i':
128                 interface = std::string{optarg};
129                 if (!checkInterface(interface))
130                 {
131                     usage(argv[0]);
132                     exit(EXIT_FAILURE);
133                 }
134                 break;
135             case 'm':
136                 imagePath = std::string{optarg};
137                 break;
138             case 's':
139                 signaturePath = std::string{optarg};
140                 break;
141             case 'a':
142                 address = std::strtol(&optarg[0], &valueEnd, 0);
143                 if (valueEnd == nullptr)
144                 {
145                     usage(argv[0]);
146                     exit(EXIT_FAILURE);
147                 }
148                 if (address > std::numeric_limits<std::uint32_t>::max())
149                 {
150                     std::fprintf(stderr, "Address beyond 32-bit limit.\n");
151                     usage(argv[0]);
152                     exit(EXIT_FAILURE);
153                 }
154                 hostAddress = static_cast<std::uint32_t>(address);
155                 break;
156             case 'l':
157                 length = std::strtol(&optarg[0], &valueEnd, 0);
158                 if (valueEnd == nullptr)
159                 {
160                     usage(argv[0]);
161                     exit(EXIT_FAILURE);
162                 }
163                 if (length > std::numeric_limits<std::uint32_t>::max())
164                 {
165                     std::fprintf(stderr, "Length beyond 32-bit limit.\n");
166                     usage(argv[0]);
167                     exit(EXIT_FAILURE);
168                 }
169                 hostLength = static_cast<std::uint32_t>(length);
170                 break;
171             case 't':
172                 type = std::string{optarg};
173                 break;
174             case 'u':
175                 ignoreUpdate = true;
176                 break;
177             default:
178                 usage(argv[0]);
179                 exit(EXIT_FAILURE);
180         }
181     }
182 
183     if (command.empty())
184     {
185         usage(argv[0]);
186         exit(EXIT_FAILURE);
187     }
188 
189     /* They want to update the firmware. */
190     if (command == "update")
191     {
192         if (interface.empty() || imagePath.empty() || signaturePath.empty() ||
193             type.empty())
194         {
195             usage(argv[0]);
196             exit(EXIT_FAILURE);
197         }
198 
199         auto ipmi = ipmiblob::IpmiHandler::CreateIpmiHandler();
200         ipmiblob::BlobHandler blob(std::move(ipmi));
201         host_tool::DevMemDevice devmem;
202         host_tool::PciUtilImpl pci;
203         host_tool::ProgressStdoutIndicator progress;
204 
205         std::unique_ptr<host_tool::DataInterface> handler;
206 
207         /* Input has already been validated in this case. */
208         if (interface == IPMIBT)
209         {
210             handler =
211                 std::make_unique<host_tool::BtDataHandler>(&blob, &progress);
212         }
213         else if (interface == IPMILPC)
214         {
215             if (hostAddress == 0 || hostLength == 0)
216             {
217                 std::fprintf(stderr, "Address or Length were 0\n");
218                 exit(EXIT_FAILURE);
219             }
220             handler = std::make_unique<host_tool::LpcDataHandler>(
221                 &blob, &devmem, hostAddress, hostLength, &progress);
222         }
223         else if (interface == IPMIPCI)
224         {
225             handler = std::make_unique<host_tool::P2aDataHandler>(
226                 &blob, &devmem, &pci, &progress);
227         }
228 
229         if (!handler)
230         {
231             /* TODO(venture): use a custom exception. */
232             std::fprintf(stderr, "Interface %s is unavailable\n",
233                          interface.c_str());
234             exit(EXIT_FAILURE);
235         }
236 
237         /* The parameters are all filled out. */
238         try
239         {
240             host_tool::UpdateHandler updater(&blob, handler.get());
241             host_tool::updaterMain(&updater, imagePath, signaturePath, type,
242                                    ignoreUpdate);
243         }
244         catch (const host_tool::ToolException& e)
245         {
246             std::fprintf(stderr, "Exception received: %s\n", e.what());
247             return -1;
248         }
249         catch (const std::exception& e)
250         {
251             std::fprintf(stderr, "Unexpected exception received: %s\n",
252                          e.what());
253             return -1;
254         }
255     }
256 
257     return 0;
258 }
259