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, &blob, imagePath, signaturePath, 278 type, 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