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