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