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