| Vernon Mauery | 52ce662 | 2019-05-22 09:19:46 -0700 | [diff] [blame] | 1 | #include <ipmid/api.h> | 
|  | 2 | #include <openssl/evp.h> | 
|  | 3 | #include <openssl/sha.h> | 
|  | 4 | #include <sys/mman.h> | 
|  | 5 | #include <sys/stat.h> | 
|  | 6 | #include <sys/types.h> | 
|  | 7 | #include <unistd.h> | 
|  | 8 |  | 
|  | 9 | #include <boost/algorithm/string.hpp> | 
|  | 10 | #include <boost/asio.hpp> | 
|  | 11 | #include <boost/process/child.hpp> | 
|  | 12 | #include <boost/uuid/random_generator.hpp> | 
|  | 13 | #include <boost/uuid/uuid_io.hpp> | 
|  | 14 | #include <chrono> | 
|  | 15 | #include <commandutils.hpp> | 
|  | 16 | #include <cstdint> | 
|  | 17 | #include <filesystem> | 
|  | 18 | #include <fstream> | 
|  | 19 | #include <iostream> | 
|  | 20 | #include <map> | 
|  | 21 | #include <random> | 
|  | 22 | #include <sdbusplus/bus.hpp> | 
|  | 23 | #include <sdbusplus/bus/match.hpp> | 
|  | 24 | #include <sdbusplus/server/object.hpp> | 
|  | 25 | #include <sdbusplus/timer.hpp> | 
|  | 26 | #include <sstream> | 
|  | 27 |  | 
|  | 28 | static void register_netfn_firmware_functions() __attribute__((constructor)); | 
|  | 29 |  | 
|  | 30 | // oem return code for firmware update control | 
|  | 31 | constexpr ipmi_ret_t IPMI_CC_REQ_INVALID_PHASE = 0xd5; | 
|  | 32 | constexpr ipmi_ret_t IPMI_CC_USB_ATTACH_FAIL = 0x80; | 
|  | 33 |  | 
| Vernon Mauery | 52ce662 | 2019-05-22 09:19:46 -0700 | [diff] [blame] | 34 | static constexpr bool DEBUG = false; | 
|  | 35 |  | 
|  | 36 | static constexpr char FW_UPDATE_SERVER_DBUS_NAME[] = | 
|  | 37 | "xyz.openbmc_project.fwupdate1.server"; | 
|  | 38 |  | 
|  | 39 | static constexpr char FW_UPDATE_SERVER_PATH[] = | 
|  | 40 | "/xyz/openbmc_project/fwupdate1"; | 
|  | 41 | static constexpr char FW_UPDATE_SERVER_INFO_PATH[] = | 
|  | 42 | "/xyz/openbmc_project/fwupdate1/info"; | 
|  | 43 | static constexpr char FW_UPDATE_ACTIVE_INFO_PATH[] = | 
|  | 44 | "/xyz/openbmc_project/fwupdate1/info/bmc_active"; | 
|  | 45 | static constexpr char FW_UPDATE_BACKUP_INFO_PATH[] = | 
|  | 46 | "/xyz/openbmc_project/fwupdate1/info/bmc_backup"; | 
|  | 47 |  | 
|  | 48 | static constexpr char FW_UPDATE_INTERFACE[] = "xyz.openbmc_project.fwupdate1"; | 
|  | 49 | static constexpr char FW_UPDATE_INFO_INTERFACE[] = | 
|  | 50 | "xyz.openbmc_project.fwupdate1.fwinfo"; | 
|  | 51 | static constexpr char FW_UPDATE_SECURITY_INTERFACE[] = | 
|  | 52 | "xyz.openbmc_project.fwupdate1.security"; | 
|  | 53 |  | 
|  | 54 | constexpr std::size_t operator""_MB(unsigned long long v) | 
|  | 55 | { | 
|  | 56 | return 1024u * 1024u * v; | 
|  | 57 | } | 
|  | 58 | static constexpr int FIRMWARE_BUFFER_MAX_SIZE = 32_MB; | 
|  | 59 |  | 
|  | 60 | static constexpr char FIRMWARE_BUFFER_FILE[] = "/tmp/fw-download.bin"; | 
|  | 61 | static bool local_download_is_active(void) | 
|  | 62 | { | 
|  | 63 | struct stat sb; | 
|  | 64 | if (stat(FIRMWARE_BUFFER_FILE, &sb) < 0) | 
|  | 65 | return false; | 
|  | 66 | return true; | 
|  | 67 | } | 
|  | 68 |  | 
|  | 69 | class fw_update_status_cache | 
|  | 70 | { | 
|  | 71 | public: | 
|  | 72 | enum | 
|  | 73 | { | 
|  | 74 | FW_STATE_INIT = 0, | 
|  | 75 | FW_STATE_IDLE, | 
|  | 76 | FW_STATE_DOWNLOAD, | 
|  | 77 | FW_STATE_VERIFY, | 
|  | 78 | FW_STATE_WRITE, | 
|  | 79 | FW_STATE_READY, | 
|  | 80 | FW_STATE_ERROR = 0x0f, | 
|  | 81 | FW_STATE_AC_CYCLE_REQUIRED = 0x83, | 
|  | 82 | }; | 
| Vernon Mauery | 15419dd | 2019-05-24 09:40:30 -0700 | [diff] [blame] | 83 | fw_update_status_cache() : _bus(getSdBus()) | 
| Vernon Mauery | 52ce662 | 2019-05-22 09:19:46 -0700 | [diff] [blame] | 84 | { | 
|  | 85 | _match = std::make_shared<sdbusplus::bus::match::match>( | 
| Vernon Mauery | 15419dd | 2019-05-24 09:40:30 -0700 | [diff] [blame] | 86 | *_bus, | 
| Vernon Mauery | 52ce662 | 2019-05-22 09:19:46 -0700 | [diff] [blame] | 87 | sdbusplus::bus::match::rules::propertiesChanged( | 
|  | 88 | FW_UPDATE_SERVER_PATH, FW_UPDATE_INTERFACE), | 
|  | 89 | [&](sdbusplus::message::message &msg) { | 
|  | 90 | if (DEBUG) | 
|  | 91 | std::cerr << "propertiesChanged lambda\n"; | 
|  | 92 | std::map<std::string, ipmi::DbusVariant> props; | 
|  | 93 | std::vector<std::string> inval; | 
|  | 94 | std::string iface; | 
|  | 95 | msg.read(iface, props, inval); | 
|  | 96 | _parse_props(props); | 
|  | 97 | }); | 
|  | 98 | _initial_fetch(); | 
|  | 99 | } | 
|  | 100 |  | 
|  | 101 | uint8_t state() | 
|  | 102 | { | 
|  | 103 | if (DEBUG) | 
|  | 104 | std::cerr << "fw-state: 0x" << std::hex << (int)_state << '\n'; | 
|  | 105 | if ((_state == FW_STATE_IDLE || _state == FW_STATE_INIT) && | 
|  | 106 | local_download_is_active()) | 
|  | 107 | { | 
|  | 108 | _state = FW_STATE_DOWNLOAD; | 
|  | 109 | _percent = 0; | 
|  | 110 | } | 
|  | 111 | return _state; | 
|  | 112 | } | 
|  | 113 | uint8_t percent() | 
|  | 114 | { | 
|  | 115 | return _percent; | 
|  | 116 | } | 
|  | 117 | std::string msg() | 
|  | 118 | { | 
|  | 119 | return _msg; | 
|  | 120 | } | 
|  | 121 | #ifdef UPDATER_ENABLED | 
|  | 122 | std::string get_software_obj_path() | 
|  | 123 | { | 
|  | 124 | return _software_obj_path; | 
|  | 125 | } | 
|  | 126 | void set_software_obj_path(std::string &obj_path) | 
|  | 127 | { | 
|  | 128 | _software_obj_path = obj_path; | 
|  | 129 | _state = FW_STATE_WRITE; | 
|  | 130 | _percent = 0; | 
|  | 131 | _match = std::make_shared<sdbusplus::bus::match::match>( | 
| Vernon Mauery | 15419dd | 2019-05-24 09:40:30 -0700 | [diff] [blame] | 132 | *_bus, | 
| Vernon Mauery | 52ce662 | 2019-05-22 09:19:46 -0700 | [diff] [blame] | 133 | sdbusplus::bus::match::rules::propertiesChanged( | 
|  | 134 | _software_obj_path, | 
|  | 135 | "xyz.openbmc_project.Software.ActivationProgress"), | 
|  | 136 | [&](sdbusplus::message::message &msg) { | 
|  | 137 | if (DEBUG) | 
|  | 138 | std::cerr << "propertiesChanged lambda\n"; | 
|  | 139 | std::map<std::string, ipmi::DbusVariant> props; | 
|  | 140 | std::vector<std::string> inval; | 
|  | 141 | std::string iface; | 
|  | 142 | msg.read(iface, props, inval); | 
|  | 143 | _parse_props(props); | 
|  | 144 | }); | 
|  | 145 | } | 
|  | 146 | uint8_t activation_timer_timeout() | 
|  | 147 | { | 
|  | 148 | std::cerr << "activation_timer_timout(): increase percentage...\n"; | 
|  | 149 | _percent = _percent + 5; | 
|  | 150 | std::cerr << "_percent = " << std::string((char *)&_percent) << "\n"; | 
|  | 151 | return _percent; | 
|  | 152 | } | 
|  | 153 | #endif | 
|  | 154 | protected: | 
|  | 155 | void _parse_props(std::map<std::string, ipmi::DbusVariant> &properties) | 
|  | 156 | { | 
|  | 157 | if (DEBUG) | 
|  | 158 | std::cerr << "propertiesChanged (" << properties.size() | 
|  | 159 | << " elements)"; | 
|  | 160 | for (const auto &t : properties) | 
|  | 161 | { | 
|  | 162 | auto key = t.first; | 
|  | 163 | auto value = t.second; | 
|  | 164 | if (key == "state") | 
|  | 165 | { | 
| Vernon Mauery | 8166c8d | 2019-05-23 11:22:30 -0700 | [diff] [blame] | 166 | auto state = std::get<std::string>(value); | 
| Vernon Mauery | 52ce662 | 2019-05-22 09:19:46 -0700 | [diff] [blame] | 167 | if (DEBUG) | 
|  | 168 | std::cerr << ", state=" << state; | 
|  | 169 | if (state == "INIT") | 
|  | 170 | _state = FW_STATE_INIT; | 
|  | 171 | else if (state == "IDLE") | 
|  | 172 | _state = FW_STATE_IDLE; | 
|  | 173 | else if (state == "DOWNLOAD") | 
|  | 174 | _state = FW_STATE_DOWNLOAD; | 
|  | 175 | else if (state == "VERIFY") | 
|  | 176 | _state = FW_STATE_VERIFY; | 
|  | 177 | else if (state == "WRITE") | 
|  | 178 | _state = FW_STATE_WRITE; | 
|  | 179 | else if (state == "READY") | 
|  | 180 | _state = FW_STATE_READY; | 
|  | 181 | else if (state == "ERROR") | 
|  | 182 | _state = FW_STATE_ERROR; | 
|  | 183 | else if (state == "AC_CYCLE_REQUIRED") | 
|  | 184 | _state = FW_STATE_AC_CYCLE_REQUIRED; | 
|  | 185 | else | 
|  | 186 | { | 
|  | 187 | _state = FW_STATE_ERROR; | 
|  | 188 | _msg = "internal error"; | 
|  | 189 | } | 
|  | 190 | } | 
|  | 191 | else if (key == "percent") | 
|  | 192 | { | 
| Vernon Mauery | 8166c8d | 2019-05-23 11:22:30 -0700 | [diff] [blame] | 193 | _percent = std::get<int32_t>(value); | 
| Vernon Mauery | 52ce662 | 2019-05-22 09:19:46 -0700 | [diff] [blame] | 194 | if (DEBUG) | 
|  | 195 | std::cerr << ", pct=" << (int)_percent; | 
|  | 196 | } | 
|  | 197 | else if (key == "msg") | 
|  | 198 | { | 
| Vernon Mauery | 8166c8d | 2019-05-23 11:22:30 -0700 | [diff] [blame] | 199 | _msg = std::get<std::string>(value); | 
| Vernon Mauery | 52ce662 | 2019-05-22 09:19:46 -0700 | [diff] [blame] | 200 | if (DEBUG) | 
|  | 201 | std::cerr << ", msg='" << _msg << '\''; | 
|  | 202 | } | 
|  | 203 | else if (key == "Progress") | 
|  | 204 | { | 
| Vernon Mauery | 8166c8d | 2019-05-23 11:22:30 -0700 | [diff] [blame] | 205 | _percent = std::get<uint8_t>(value); | 
| Vernon Mauery | 52ce662 | 2019-05-22 09:19:46 -0700 | [diff] [blame] | 206 | ; | 
|  | 207 | if (_percent == 100) | 
|  | 208 | _state = FW_STATE_READY; | 
|  | 209 | } | 
|  | 210 | } | 
|  | 211 | if ((_state == FW_STATE_IDLE || _state == FW_STATE_INIT) && | 
|  | 212 | local_download_is_active()) | 
|  | 213 | { | 
|  | 214 | _state = FW_STATE_DOWNLOAD; | 
|  | 215 | _percent = 0; | 
|  | 216 | } | 
|  | 217 | if (DEBUG) | 
|  | 218 | std::cerr << '\n'; | 
|  | 219 | } | 
|  | 220 | void _initial_fetch() | 
|  | 221 | { | 
|  | 222 | #ifndef UPDATER_ENABLED | 
| Vernon Mauery | 15419dd | 2019-05-24 09:40:30 -0700 | [diff] [blame] | 223 | auto method = _bus->new_method_call( | 
| Vernon Mauery | 52ce662 | 2019-05-22 09:19:46 -0700 | [diff] [blame] | 224 | FW_UPDATE_SERVER_DBUS_NAME, FW_UPDATE_SERVER_PATH, | 
|  | 225 | "org.freedesktop.DBus.Properties", "GetAll"); | 
|  | 226 | method.append(FW_UPDATE_INTERFACE); | 
|  | 227 | if (DEBUG) | 
|  | 228 | std::cerr << "fetch fw status via dbus...\n"; | 
|  | 229 | try | 
|  | 230 | { | 
| Vernon Mauery | 15419dd | 2019-05-24 09:40:30 -0700 | [diff] [blame] | 231 | auto reply = _bus->call(method); | 
| Vernon Mauery | 52ce662 | 2019-05-22 09:19:46 -0700 | [diff] [blame] | 232 |  | 
|  | 233 | std::map<std::string, ipmi::DbusVariant> properties; | 
|  | 234 | reply.read(properties); | 
|  | 235 | _parse_props(properties); | 
|  | 236 | } | 
|  | 237 | catch (sdbusplus::exception::SdBusError &e) | 
|  | 238 | { | 
|  | 239 | std::cerr << "Failed in _initial_fetch(): SDBus Error: " << e.what() | 
|  | 240 | << "\n"; | 
|  | 241 | return; | 
|  | 242 | } | 
|  | 243 | #endif | 
|  | 244 | } | 
|  | 245 |  | 
| Vernon Mauery | 15419dd | 2019-05-24 09:40:30 -0700 | [diff] [blame] | 246 | std::shared_ptr<sdbusplus::asio::connection> _bus; | 
| Vernon Mauery | 52ce662 | 2019-05-22 09:19:46 -0700 | [diff] [blame] | 247 | std::shared_ptr<sdbusplus::bus::match::match> _match; | 
|  | 248 | uint8_t _state = 0; | 
|  | 249 | uint8_t _percent = 0; | 
|  | 250 | std::string _msg; | 
|  | 251 |  | 
|  | 252 | private: | 
|  | 253 | std::string _software_obj_path; | 
|  | 254 | }; | 
|  | 255 |  | 
|  | 256 | static fw_update_status_cache fw_update_status; | 
|  | 257 |  | 
|  | 258 | static std::chrono::steady_clock::time_point fw_random_number_timestamp; | 
|  | 259 | static constexpr int FW_RANDOM_NUMBER_LENGTH = 8; | 
|  | 260 | static constexpr auto FW_RANDOM_NUMBER_TTL = std::chrono::seconds(30); | 
|  | 261 | static uint8_t fw_random_number[FW_RANDOM_NUMBER_LENGTH]; | 
|  | 262 |  | 
|  | 263 | static ipmi_ret_t ipmi_firmware_get_fw_random_number( | 
|  | 264 | ipmi_netfn_t netfn, ipmi_cmd_t cmd, ipmi_request_t request, | 
|  | 265 | ipmi_response_t response, ipmi_data_len_t data_len, ipmi_context_t context) | 
|  | 266 | { | 
|  | 267 | std::random_device rd; | 
|  | 268 | std::default_random_engine gen(rd()); | 
|  | 269 | std::uniform_int_distribution<> dist{0, 255}; | 
|  | 270 |  | 
|  | 271 | if (*data_len != 0) | 
|  | 272 | { | 
|  | 273 | *data_len = 0; | 
|  | 274 | return IPMI_CC_REQ_DATA_LEN_INVALID; | 
|  | 275 | } | 
|  | 276 |  | 
|  | 277 | fw_random_number_timestamp = std::chrono::steady_clock::now(); | 
|  | 278 |  | 
|  | 279 | uint8_t *msg_reply = static_cast<uint8_t *>(response); | 
|  | 280 | for (int i = 0; i < FW_RANDOM_NUMBER_LENGTH; i++) | 
|  | 281 | fw_random_number[i] = msg_reply[i] = dist(gen); | 
|  | 282 |  | 
|  | 283 | if (DEBUG) | 
|  | 284 | std::cerr << "FW Rand Num: 0x" << std::hex << (int)msg_reply[0] << " 0x" | 
|  | 285 | << (int)msg_reply[1] << " 0x" << (int)msg_reply[2] << " 0x" | 
|  | 286 | << (int)msg_reply[3] << " 0x" << (int)msg_reply[4] << " 0x" | 
|  | 287 | << (int)msg_reply[5] << " 0x" << (int)msg_reply[6] << " 0x" | 
|  | 288 | << (int)msg_reply[7] << '\n'; | 
|  | 289 |  | 
|  | 290 | *data_len = FW_RANDOM_NUMBER_LENGTH; | 
|  | 291 |  | 
|  | 292 | return IPMI_CC_OK; | 
|  | 293 | } | 
|  | 294 |  | 
|  | 295 | static ipmi_ret_t ipmi_firmware_enter_fw_transfer_mode( | 
|  | 296 | ipmi_netfn_t netfn, ipmi_cmd_t cmd, ipmi_request_t request, | 
|  | 297 | ipmi_response_t response, ipmi_data_len_t data_len, ipmi_context_t context) | 
|  | 298 | { | 
|  | 299 | if (DEBUG) | 
|  | 300 | std::cerr << "Enter FW transfer mode requested, data_len = " | 
|  | 301 | << *data_len << '\n'; | 
|  | 302 |  | 
|  | 303 | if (*data_len != FW_RANDOM_NUMBER_LENGTH) | 
|  | 304 | { | 
|  | 305 | *data_len = 0; | 
|  | 306 | return IPMI_CC_REQ_DATA_LEN_INVALID; | 
|  | 307 | } | 
|  | 308 | *data_len = 0; | 
|  | 309 |  | 
|  | 310 | auto rq_time = std::chrono::steady_clock::now(); | 
|  | 311 | if (DEBUG) | 
|  | 312 | std::cerr << "now - fwts = " | 
|  | 313 | << std::chrono::duration_cast<std::chrono::microseconds>( | 
|  | 314 | rq_time - fw_random_number_timestamp) | 
|  | 315 | .count() | 
|  | 316 | << " us\n"; | 
|  | 317 | if (std::chrono::duration_cast<std::chrono::microseconds>( | 
|  | 318 | rq_time - fw_random_number_timestamp) | 
|  | 319 | .count() > std::chrono::duration_cast<std::chrono::microseconds>( | 
|  | 320 | FW_RANDOM_NUMBER_TTL) | 
|  | 321 | .count()) | 
|  | 322 | { | 
|  | 323 | if (DEBUG) | 
|  | 324 | std::cerr << "key timeout\n"; | 
|  | 325 | return IPMI_CC_PARM_OUT_OF_RANGE; | 
|  | 326 | } | 
|  | 327 |  | 
|  | 328 | uint8_t *msg_request = static_cast<uint8_t *>(request); | 
|  | 329 | for (int i = 0; i < FW_RANDOM_NUMBER_LENGTH; i++) | 
|  | 330 | { | 
|  | 331 | if (fw_random_number[i] != msg_request[i]) | 
|  | 332 | { | 
|  | 333 | if (DEBUG) | 
|  | 334 | std::cerr << "key error" << (int)fw_random_number[i] | 
|  | 335 | << "!=" << (int)msg_request[i] << "\n"; | 
|  | 336 | return IPMI_CC_INVALID_FIELD_REQUEST; | 
|  | 337 | } | 
|  | 338 | } | 
|  | 339 |  | 
|  | 340 | if (fw_update_status.state() != fw_update_status_cache::FW_STATE_IDLE | 
|  | 341 | // TODO: Allowing FW_STATE_INIT here to let image activation available | 
|  | 342 | // without being in FW_STATE_IDLE, need to fix/adjust the state machine | 
|  | 343 | // to match xyz.openbmc_project.Software.BMC.Updater service activation | 
|  | 344 | // mechanism at finer grain | 
|  | 345 | && fw_update_status.state() != fw_update_status_cache::FW_STATE_INIT) | 
|  | 346 | { | 
|  | 347 | if (DEBUG) | 
|  | 348 | std::cerr << "not in INIT or IDLE\n"; | 
|  | 349 | return IPMI_CC_INVALID_FIELD_REQUEST; | 
|  | 350 | } | 
|  | 351 | // FIXME? c++ doesn't off an option for exclusive file creation | 
|  | 352 | FILE *fp = fopen(FIRMWARE_BUFFER_FILE, "wx"); | 
|  | 353 | if (!fp) | 
|  | 354 | { | 
|  | 355 | if (DEBUG) | 
|  | 356 | std::cerr << "failed to create buffer file\n"; | 
|  | 357 | return IPMI_CC_INVALID_FIELD_REQUEST; | 
|  | 358 | } | 
|  | 359 | fclose(fp); | 
|  | 360 |  | 
|  | 361 | return IPMI_CC_OK; | 
|  | 362 | } | 
|  | 363 |  | 
|  | 364 | static ipmi_ret_t ipmi_firmware_exit_fw_update_mode( | 
|  | 365 | ipmi_netfn_t netfn, ipmi_cmd_t cmd, ipmi_request_t request, | 
|  | 366 | ipmi_response_t response, ipmi_data_len_t data_len, ipmi_context_t context) | 
|  | 367 | { | 
|  | 368 | if (DEBUG) | 
|  | 369 | std::cerr << "Exit FW update mode\n"; | 
|  | 370 | *data_len = 0; | 
|  | 371 |  | 
|  | 372 | ipmi_ret_t rc = IPMI_CC_OK; | 
|  | 373 | switch (fw_update_status.state()) | 
|  | 374 | { | 
|  | 375 | case fw_update_status_cache::FW_STATE_INIT: | 
|  | 376 | case fw_update_status_cache::FW_STATE_IDLE: | 
|  | 377 | rc = IPMI_CC_INVALID_FIELD_REQUEST; | 
|  | 378 | break; | 
|  | 379 | case fw_update_status_cache::FW_STATE_DOWNLOAD: | 
|  | 380 | unlink(FIRMWARE_BUFFER_FILE); | 
|  | 381 | case fw_update_status_cache::FW_STATE_VERIFY: | 
|  | 382 | break; | 
|  | 383 | case fw_update_status_cache::FW_STATE_WRITE: | 
|  | 384 | rc = IPMI_CC_INVALID_FIELD_REQUEST; | 
|  | 385 | break; | 
|  | 386 | case fw_update_status_cache::FW_STATE_READY: | 
|  | 387 | case fw_update_status_cache::FW_STATE_ERROR: | 
|  | 388 | unlink(FIRMWARE_BUFFER_FILE); | 
|  | 389 | break; | 
|  | 390 | case fw_update_status_cache::FW_STATE_AC_CYCLE_REQUIRED: | 
|  | 391 | rc = IPMI_CC_INVALID_FIELD_REQUEST; | 
|  | 392 | break; | 
|  | 393 | } | 
|  | 394 | if (rc == IPMI_CC_OK) | 
|  | 395 | { | 
| Vernon Mauery | 15419dd | 2019-05-24 09:40:30 -0700 | [diff] [blame] | 396 | std::shared_ptr<sdbusplus::asio::connection> bus = getSdBus(); | 
| Vernon Mauery | 52ce662 | 2019-05-22 09:19:46 -0700 | [diff] [blame] | 397 | // attempt to reset the state machine -- this may fail | 
| Vernon Mauery | 15419dd | 2019-05-24 09:40:30 -0700 | [diff] [blame] | 398 | auto method = bus->new_method_call( | 
| Vernon Mauery | 52ce662 | 2019-05-22 09:19:46 -0700 | [diff] [blame] | 399 | FW_UPDATE_SERVER_DBUS_NAME, FW_UPDATE_SERVER_PATH, | 
|  | 400 | "org.freedesktop.DBus.Properties", "Abort"); | 
|  | 401 | try | 
|  | 402 | { | 
| Vernon Mauery | 15419dd | 2019-05-24 09:40:30 -0700 | [diff] [blame] | 403 | auto reply = bus->call(method); | 
| Vernon Mauery | 52ce662 | 2019-05-22 09:19:46 -0700 | [diff] [blame] | 404 |  | 
|  | 405 | ipmi::DbusVariant retval; | 
|  | 406 | reply.read(retval); | 
| Vernon Mauery | 8166c8d | 2019-05-23 11:22:30 -0700 | [diff] [blame] | 407 | if (std::get<int>(retval) != 0) | 
| Vernon Mauery | 52ce662 | 2019-05-22 09:19:46 -0700 | [diff] [blame] | 408 | rc = IPMI_CC_INVALID_FIELD_REQUEST; | 
|  | 409 | } | 
|  | 410 | catch (sdbusplus::exception::SdBusError &e) | 
|  | 411 | { | 
|  | 412 | std::cerr << "SDBus Error: " << e.what() << "\n"; | 
|  | 413 | return IPMI_CC_UNSPECIFIED_ERROR; | 
|  | 414 | } | 
|  | 415 | } | 
|  | 416 |  | 
|  | 417 | return rc; | 
|  | 418 | } | 
|  | 419 |  | 
|  | 420 | static void post_transfer_complete_handler( | 
|  | 421 | std::unique_ptr<sdbusplus::bus::match::match> &fw_update_matcher); | 
|  | 422 | static bool request_start_firmware_update(const std::string &uri) | 
|  | 423 | { | 
|  | 424 | if (DEBUG) | 
|  | 425 | std::cerr << "request start firmware update()\n"; | 
|  | 426 |  | 
|  | 427 | #ifdef UPDATER_ENABLED | 
|  | 428 | // fwupdate URIs start with file:// or usb:// or tftp:// etc. By the time | 
|  | 429 | // the code gets to this point, the file should be transferred start the | 
|  | 430 | // request (creating a new file in /tmp/images causes the update manager to | 
|  | 431 | // check if it is ready for activation) | 
|  | 432 | static std::unique_ptr<sdbusplus::bus::match::match> fw_update_matcher; | 
|  | 433 | post_transfer_complete_handler(fw_update_matcher); | 
|  | 434 | std::filesystem::rename( | 
|  | 435 | uri, "/tmp/images/" + | 
|  | 436 | boost::uuids::to_string(boost::uuids::random_generator()())); | 
|  | 437 | #else | 
| Vernon Mauery | 15419dd | 2019-05-24 09:40:30 -0700 | [diff] [blame] | 438 | std::shared_ptr<sdbusplus::asio::connection> bus = getSdBus(); | 
| Vernon Mauery | 52ce662 | 2019-05-22 09:19:46 -0700 | [diff] [blame] | 439 | auto method = | 
| Vernon Mauery | 15419dd | 2019-05-24 09:40:30 -0700 | [diff] [blame] | 440 | bus->new_method_call(FW_UPDATE_SERVER_DBUS_NAME, FW_UPDATE_SERVER_PATH, | 
| Vernon Mauery | 52ce662 | 2019-05-22 09:19:46 -0700 | [diff] [blame] | 441 | FW_UPDATE_INTERFACE, "start"); | 
|  | 442 | if (DEBUG) | 
|  | 443 | std::cerr << "fwupdate1.start: " << uri << '\n'; | 
|  | 444 | method.append(uri); | 
|  | 445 | try | 
|  | 446 | { | 
| Vernon Mauery | 15419dd | 2019-05-24 09:40:30 -0700 | [diff] [blame] | 447 | auto reply = bus->call(method); | 
| Vernon Mauery | 52ce662 | 2019-05-22 09:19:46 -0700 | [diff] [blame] | 448 |  | 
|  | 449 | uint32_t retval; | 
|  | 450 | reply.read(retval); | 
|  | 451 | if (retval != 0) | 
|  | 452 | { | 
|  | 453 | if (DEBUG) | 
|  | 454 | std::cerr << "method returned non-zero: " << retval << "\n"; | 
|  | 455 | return false; | 
|  | 456 | } | 
|  | 457 | return true; | 
|  | 458 | } | 
|  | 459 | catch (sdbusplus::exception::SdBusError &e) | 
|  | 460 | { | 
|  | 461 | std::cerr << "SDBus Error: " << e.what(); | 
|  | 462 | return false; | 
|  | 463 | } | 
|  | 464 | #endif | 
|  | 465 | return true; | 
|  | 466 | } | 
|  | 467 |  | 
|  | 468 | static bool request_abort_firmware_update() | 
|  | 469 | { | 
| Vernon Mauery | 15419dd | 2019-05-24 09:40:30 -0700 | [diff] [blame] | 470 | std::shared_ptr<sdbusplus::asio::connection> bus = getSdBus(); | 
| Vernon Mauery | 52ce662 | 2019-05-22 09:19:46 -0700 | [diff] [blame] | 471 | auto method = | 
| Vernon Mauery | 15419dd | 2019-05-24 09:40:30 -0700 | [diff] [blame] | 472 | bus->new_method_call(FW_UPDATE_SERVER_DBUS_NAME, FW_UPDATE_SERVER_PATH, | 
| Vernon Mauery | 52ce662 | 2019-05-22 09:19:46 -0700 | [diff] [blame] | 473 | FW_UPDATE_INTERFACE, "abort"); | 
|  | 474 | try | 
|  | 475 | { | 
| Vernon Mauery | 15419dd | 2019-05-24 09:40:30 -0700 | [diff] [blame] | 476 | auto reply = bus->call(method); | 
| Vernon Mauery | 52ce662 | 2019-05-22 09:19:46 -0700 | [diff] [blame] | 477 |  | 
|  | 478 | unlink(FIRMWARE_BUFFER_FILE); | 
|  | 479 | uint32_t retval; | 
|  | 480 | reply.read(retval); | 
|  | 481 | if (retval != 0) | 
|  | 482 | return false; | 
|  | 483 | } | 
|  | 484 | catch (sdbusplus::exception::SdBusError &e) | 
|  | 485 | { | 
|  | 486 | std::cerr << "SDBus Error: " << e.what() << "\n"; | 
|  | 487 | unlink(FIRMWARE_BUFFER_FILE); | 
|  | 488 | return false; | 
|  | 489 | } | 
|  | 490 | return true; | 
|  | 491 | } | 
|  | 492 |  | 
|  | 493 | class transfer_hash_check | 
|  | 494 | { | 
|  | 495 | public: | 
|  | 496 | enum hash_check | 
|  | 497 | { | 
|  | 498 | CHECK_NOT_REQUESTED = 0, | 
|  | 499 | CHECK_REQUESTED, | 
|  | 500 | CHECK_PASSED_SHA2, | 
|  | 501 | CHECK_RESVD1, | 
|  | 502 | CHECK_FAILED_SHA2 = 0xe2, | 
|  | 503 | CHECK_RESVD2 = 0xe3, | 
|  | 504 | }; | 
|  | 505 |  | 
|  | 506 | protected: | 
|  | 507 | EVP_MD_CTX *_ctx; | 
|  | 508 | std::vector<uint8_t> _expected; | 
|  | 509 | enum hash_check _check; | 
|  | 510 | bool _started; | 
|  | 511 |  | 
|  | 512 | public: | 
|  | 513 | transfer_hash_check() : _check(CHECK_NOT_REQUESTED), _started(false) | 
|  | 514 | { | 
|  | 515 | } | 
|  | 516 | ~transfer_hash_check() | 
|  | 517 | { | 
|  | 518 | if (_ctx) | 
|  | 519 | { | 
|  | 520 | EVP_MD_CTX_destroy(_ctx); | 
|  | 521 | _ctx = NULL; | 
|  | 522 | } | 
|  | 523 | } | 
|  | 524 | void init(const std::vector<uint8_t> &expected) | 
|  | 525 | { | 
|  | 526 | _expected = expected; | 
|  | 527 | _check = CHECK_REQUESTED; | 
|  | 528 | _ctx = EVP_MD_CTX_create(); | 
|  | 529 | EVP_DigestInit(_ctx, EVP_sha256()); | 
|  | 530 | } | 
|  | 531 | void hash(const std::vector<uint8_t> &data) | 
|  | 532 | { | 
|  | 533 | if (!_started) | 
|  | 534 | _started = true; | 
|  | 535 | EVP_DigestUpdate(_ctx, data.data(), data.size()); | 
|  | 536 | } | 
|  | 537 | void clear() | 
|  | 538 | { | 
|  | 539 | // if not started, nothing to clear | 
|  | 540 | if (_started) | 
|  | 541 | { | 
|  | 542 | if (_ctx) | 
|  | 543 | EVP_MD_CTX_destroy(_ctx); | 
|  | 544 | if (_check != CHECK_NOT_REQUESTED) | 
|  | 545 | _check = CHECK_REQUESTED; | 
|  | 546 | _ctx = EVP_MD_CTX_create(); | 
|  | 547 | EVP_DigestInit(_ctx, EVP_sha256()); | 
|  | 548 | } | 
|  | 549 | } | 
|  | 550 | enum hash_check check() | 
|  | 551 | { | 
|  | 552 | if (_check == CHECK_REQUESTED) | 
|  | 553 | { | 
|  | 554 | unsigned int len; | 
|  | 555 | std::vector<uint8_t> digest(EVP_MD_size(EVP_sha256())); | 
|  | 556 | EVP_DigestFinal(_ctx, digest.data(), &len); | 
|  | 557 | if (digest == _expected) | 
|  | 558 | { | 
|  | 559 | if (DEBUG) | 
|  | 560 | std::cerr << "transfer sha2 check passed\n"; | 
|  | 561 | _check = CHECK_PASSED_SHA2; | 
|  | 562 | } | 
|  | 563 | else | 
|  | 564 | { | 
|  | 565 | if (DEBUG) | 
|  | 566 | std::cerr << "transfer sha2 check failed\n"; | 
|  | 567 | _check = CHECK_FAILED_SHA2; | 
|  | 568 | } | 
|  | 569 | } | 
|  | 570 | return _check; | 
|  | 571 | } | 
|  | 572 | uint8_t status() const | 
|  | 573 | { | 
|  | 574 | return static_cast<uint8_t>(_check); | 
|  | 575 | } | 
|  | 576 | }; | 
|  | 577 |  | 
|  | 578 | std::shared_ptr<transfer_hash_check> xfer_hash_check; | 
|  | 579 |  | 
|  | 580 | #ifdef UPDATER_ENABLED | 
|  | 581 | static void activate_image(const char *obj_path) | 
|  | 582 | { | 
|  | 583 | if (DEBUG) | 
|  | 584 | { | 
|  | 585 | std::cerr << "activateImage()...\n"; | 
|  | 586 | std::cerr << "obj_path = " << obj_path << "\n"; | 
|  | 587 | } | 
| Vernon Mauery | 15419dd | 2019-05-24 09:40:30 -0700 | [diff] [blame] | 588 | std::shared_ptr<sdbusplus::asio::connection> bus = getSdBus(); | 
|  | 589 | auto method_call = bus->new_method_call( | 
| Vernon Mauery | 52ce662 | 2019-05-22 09:19:46 -0700 | [diff] [blame] | 590 | "xyz.openbmc_project.Software.BMC.Updater", obj_path, | 
|  | 591 | "org.freedesktop.DBus.Properties", "Set"); | 
| Vernon Mauery | 8166c8d | 2019-05-23 11:22:30 -0700 | [diff] [blame] | 592 | method_call.append( | 
|  | 593 | "xyz.openbmc_project.Software.Activation", "RequestedActivation", | 
|  | 594 | std::variant<std::string>("xyz.openbmc_project.Software.Activation." | 
|  | 595 | "RequestedActivations.Active")); | 
| Vernon Mauery | 52ce662 | 2019-05-22 09:19:46 -0700 | [diff] [blame] | 596 |  | 
|  | 597 | try | 
|  | 598 | { | 
| Vernon Mauery | 15419dd | 2019-05-24 09:40:30 -0700 | [diff] [blame] | 599 | auto method_call_reply = bus->call(method_call); | 
| Vernon Mauery | 52ce662 | 2019-05-22 09:19:46 -0700 | [diff] [blame] | 600 | } | 
|  | 601 | catch (sdbusplus::exception::SdBusError &e) | 
|  | 602 | { | 
|  | 603 | std::cerr << "Failed in activate_image(): SDBus Error: " << e.what() | 
|  | 604 | << "\n"; | 
|  | 605 | return; | 
|  | 606 | } | 
|  | 607 | } | 
|  | 608 |  | 
|  | 609 | static void post_transfer_complete_handler( | 
|  | 610 | std::unique_ptr<sdbusplus::bus::match::match> &fw_update_matcher) | 
|  | 611 | { | 
|  | 612 | // Setup timer for watching signal | 
|  | 613 | static phosphor::Timer timer( | 
|  | 614 | [&fw_update_matcher]() { fw_update_matcher = nullptr; }); | 
|  | 615 |  | 
|  | 616 | static phosphor::Timer activation_status_timer([]() { | 
|  | 617 | if (fw_update_status.activation_timer_timeout() >= 95) | 
|  | 618 | { | 
|  | 619 | activation_status_timer.stop(); | 
|  | 620 | } | 
|  | 621 | }); | 
|  | 622 |  | 
|  | 623 | timer.start(std::chrono::microseconds(5000000), false); | 
|  | 624 |  | 
|  | 625 | // callback function for capturing signal | 
|  | 626 | auto callback = [&fw_update_matcher](sdbusplus::message::message &m) { | 
|  | 627 | if (DEBUG) | 
|  | 628 | std::cerr << "[complete] Match fired\n"; | 
|  | 629 | bool flag = false; | 
|  | 630 |  | 
|  | 631 | std::vector<std::pair< | 
|  | 632 | std::string, | 
| Vernon Mauery | 8166c8d | 2019-05-23 11:22:30 -0700 | [diff] [blame] | 633 | std::vector<std::pair<std::string, std::variant<std::string>>>>> | 
| Vernon Mauery | 52ce662 | 2019-05-22 09:19:46 -0700 | [diff] [blame] | 634 | interfaces_properties; | 
|  | 635 |  | 
|  | 636 | sdbusplus::message::object_path obj_path; | 
|  | 637 |  | 
|  | 638 | try | 
|  | 639 | { | 
|  | 640 | m.read(obj_path, interfaces_properties); // Read in the object path | 
|  | 641 | // that was just created | 
|  | 642 | } | 
|  | 643 | catch (std::exception &e) | 
|  | 644 | { | 
|  | 645 | std::cerr | 
|  | 646 | << "[complete] Failed at post_transfer_complete-handler : " | 
|  | 647 | << e.what() << "\n"; | 
|  | 648 | } | 
|  | 649 | // constructing response message | 
|  | 650 | if (DEBUG) | 
|  | 651 | std::cerr << "[complete] obj path = " << obj_path.str << "\n"; | 
|  | 652 | for (auto &interface : interfaces_properties) | 
|  | 653 | { | 
|  | 654 | if (DEBUG) | 
|  | 655 | std::cerr << "[complete] interface = " << interface.first | 
|  | 656 | << "\n"; | 
|  | 657 |  | 
|  | 658 | if (interface.first == "xyz.openbmc_project.Software.Activation") | 
|  | 659 | { | 
|  | 660 | // cancel timer only when | 
|  | 661 | // xyz.openbmc_project.Software.Activation interface is | 
|  | 662 | // added | 
|  | 663 |  | 
|  | 664 | if (DEBUG) | 
|  | 665 | std::cerr << "[complete] Attempt to cancel timer...\n"; | 
|  | 666 | try | 
|  | 667 | { | 
|  | 668 | timer.stop(); | 
|  | 669 | activation_status_timer.start( | 
|  | 670 | std::chrono::microseconds(3000000), true); | 
|  | 671 | } | 
|  | 672 | catch (std::exception &e) | 
|  | 673 | { | 
|  | 674 | std::cerr << "[complete] cancel timer error: " << e.what() | 
|  | 675 | << "\n"; | 
|  | 676 | } | 
|  | 677 |  | 
|  | 678 | fw_update_status.set_software_obj_path(obj_path.str); | 
|  | 679 | activate_image(obj_path.str.c_str()); | 
|  | 680 | if (DEBUG) | 
|  | 681 | std::cerr << "[complete] returned from activeImage()\n"; | 
|  | 682 |  | 
|  | 683 | fw_update_matcher = nullptr; | 
|  | 684 | } | 
|  | 685 | } | 
|  | 686 | }; | 
|  | 687 |  | 
|  | 688 | // Adding matcher | 
|  | 689 | fw_update_matcher = std::make_unique<sdbusplus::bus::match::match>( | 
| Vernon Mauery | 15419dd | 2019-05-24 09:40:30 -0700 | [diff] [blame] | 690 | *getSdBus(), | 
| Vernon Mauery | 52ce662 | 2019-05-22 09:19:46 -0700 | [diff] [blame] | 691 | "interface='org.freedesktop.DBus.ObjectManager',type='signal'," | 
|  | 692 | "member='InterfacesAdded',path='/xyz/openbmc_project/software'", | 
|  | 693 | callback); | 
|  | 694 | } | 
|  | 695 | #endif | 
|  | 696 |  | 
|  | 697 | class MappedFile | 
|  | 698 | { | 
|  | 699 | public: | 
|  | 700 | MappedFile(const std::string &fname) : addr(nullptr), fsize(0) | 
|  | 701 | { | 
|  | 702 | std::error_code ec; | 
|  | 703 | size_t sz = std::filesystem::file_size(fname, ec); | 
|  | 704 | int fd = open(fname.c_str(), O_RDONLY); | 
|  | 705 | if (!ec || fd < 0) | 
|  | 706 | { | 
|  | 707 | return; | 
|  | 708 | } | 
|  | 709 | void *tmp = mmap(NULL, sz, PROT_READ, MAP_SHARED, fd, 0); | 
|  | 710 | close(fd); | 
|  | 711 | if (tmp == MAP_FAILED) | 
|  | 712 | { | 
|  | 713 | return; | 
|  | 714 | } | 
|  | 715 | addr = tmp; | 
|  | 716 | fsize = sz; | 
|  | 717 | } | 
|  | 718 |  | 
|  | 719 | ~MappedFile() | 
|  | 720 | { | 
|  | 721 | if (addr) | 
|  | 722 | { | 
|  | 723 | munmap(addr, fsize); | 
|  | 724 | } | 
|  | 725 | } | 
|  | 726 | const uint8_t *data() const | 
|  | 727 | { | 
|  | 728 | return static_cast<const uint8_t *>(addr); | 
|  | 729 | } | 
|  | 730 | size_t size() const | 
|  | 731 | { | 
|  | 732 | return fsize; | 
|  | 733 | } | 
|  | 734 |  | 
|  | 735 | private: | 
|  | 736 | size_t fsize; | 
|  | 737 | void *addr; | 
|  | 738 | }; | 
|  | 739 |  | 
|  | 740 | static int transfer_from_file(const std::string &uri, bool move = true) | 
|  | 741 | { | 
|  | 742 | std::error_code ec; | 
|  | 743 | if (DEBUG) | 
|  | 744 | std::cerr << "transfer_from_file(" << uri << ")\n"; | 
|  | 745 | if (move) | 
|  | 746 | { | 
|  | 747 | std::filesystem::rename(uri, FIRMWARE_BUFFER_FILE, ec); | 
|  | 748 | } | 
|  | 749 | else | 
|  | 750 | { | 
|  | 751 | std::filesystem::copy(uri, FIRMWARE_BUFFER_FILE, | 
|  | 752 | std::filesystem::copy_options::overwrite_existing, | 
|  | 753 | ec); | 
|  | 754 | } | 
|  | 755 | if (xfer_hash_check) | 
|  | 756 | { | 
|  | 757 | MappedFile mappedfw(uri); | 
|  | 758 | xfer_hash_check->hash( | 
|  | 759 | {mappedfw.data(), mappedfw.data() + mappedfw.size()}); | 
|  | 760 | } | 
|  | 761 | if (ec.value()) | 
|  | 762 | { | 
|  | 763 | std::cerr << "cp/mv returns: " << ec.message() << "(" << ec.value() | 
|  | 764 | << ")\n"; | 
|  | 765 | } | 
|  | 766 | return ec.value(); | 
|  | 767 | } | 
|  | 768 |  | 
|  | 769 | template <typename... ArgTypes> | 
|  | 770 | static int executeCmd(const char *path, ArgTypes &&... tArgs) | 
|  | 771 | { | 
|  | 772 | boost::process::child execProg(path, const_cast<char *>(tArgs)...); | 
|  | 773 | execProg.wait(); | 
|  | 774 | return execProg.exit_code(); | 
|  | 775 | } | 
|  | 776 |  | 
|  | 777 | constexpr char USB_CTRL_PATH[] = "/usr/bin/usb-ctrl"; | 
|  | 778 | constexpr char FWUPDATE_MOUNT_POINT[] = "/tmp/usb-fwupd.mnt"; | 
|  | 779 | constexpr char FWUPDATE_USB_VOL_IMG[] = "/tmp/usb-fwupd.img"; | 
|  | 780 | constexpr char FWUPDATE_USB_DEV_NAME[] = "fw-usb-mass-storage-dev"; | 
|  | 781 | constexpr size_t fwPathMaxLength = 255; | 
|  | 782 | static int transfer_from_usb(const std::string &uri) | 
|  | 783 | { | 
|  | 784 | int ret, sysret; | 
|  | 785 | char fwpath[fwPathMaxLength]; | 
|  | 786 | if (DEBUG) | 
|  | 787 | std::cerr << "transfer_from_usb(" << uri << ")\n"; | 
|  | 788 | ret = executeCmd(USB_CTRL_PATH, "mount", FWUPDATE_USB_VOL_IMG, | 
|  | 789 | FWUPDATE_MOUNT_POINT); | 
|  | 790 | if (ret) | 
|  | 791 | { | 
|  | 792 | return ret; | 
|  | 793 | } | 
|  | 794 |  | 
|  | 795 | std::string usb_path = std::string(FWUPDATE_MOUNT_POINT) + "/" + uri; | 
|  | 796 | ret = transfer_from_file(usb_path, false); | 
|  | 797 |  | 
|  | 798 | executeCmd(USB_CTRL_PATH, "cleanup", FWUPDATE_USB_VOL_IMG, | 
|  | 799 | FWUPDATE_MOUNT_POINT); | 
|  | 800 | return ret; | 
|  | 801 | } | 
|  | 802 |  | 
|  | 803 | static bool transfer_firmware_from_uri(const std::string &uri) | 
|  | 804 | { | 
|  | 805 | static constexpr char FW_URI_FILE[] = "file://"; | 
|  | 806 | static constexpr char FW_URI_USB[] = "usb://"; | 
|  | 807 | if (DEBUG) | 
|  | 808 | std::cerr << "transfer_firmware_from_uri(" << uri << ")\n"; | 
|  | 809 | if (boost::algorithm::starts_with(uri, FW_URI_FILE)) | 
|  | 810 | { | 
|  | 811 | std::string fname = uri.substr(sizeof(FW_URI_FILE) - 1); | 
|  | 812 | if (fname != FIRMWARE_BUFFER_FILE) | 
|  | 813 | { | 
|  | 814 | return 0 == transfer_from_file(fname); | 
|  | 815 | } | 
|  | 816 | return true; | 
|  | 817 | } | 
|  | 818 | if (boost::algorithm::starts_with(uri, FW_URI_USB)) | 
|  | 819 | { | 
|  | 820 | std::string fname = uri.substr(sizeof(FW_URI_USB) - 1); | 
|  | 821 | return 0 == transfer_from_usb(fname); | 
|  | 822 | } | 
|  | 823 | return false; | 
|  | 824 | } | 
|  | 825 |  | 
|  | 826 | /* Get USB-mass-storage device status: inserted => true, ejected => false */ | 
|  | 827 | static int usb_get_status() | 
|  | 828 | { | 
|  | 829 | static constexpr char usb_gadget_base[] = "/sys/kernel/config/usb_gadget/"; | 
|  | 830 | auto usb_device = | 
|  | 831 | std::filesystem::path(usb_gadget_base) / FWUPDATE_USB_DEV_NAME; | 
|  | 832 | std::error_code ec; | 
|  | 833 | return std::filesystem::exists(usb_device, ec) && !ec; | 
|  | 834 | } | 
|  | 835 |  | 
|  | 836 | /* Insert the USB-mass-storage device status: success => 0, failure => non-0 */ | 
|  | 837 | static int usb_attach_device() | 
|  | 838 | { | 
|  | 839 | if (usb_get_status()) | 
|  | 840 | { | 
|  | 841 | return 1; | 
|  | 842 | } | 
|  | 843 | int ret = | 
|  | 844 | executeCmd(USB_CTRL_PATH, "setup", FWUPDATE_USB_VOL_IMG, | 
|  | 845 | std::to_string(FIRMWARE_BUFFER_MAX_SIZE / 1_MB).c_str()); | 
|  | 846 | if (!ret) | 
|  | 847 | { | 
|  | 848 | ret = executeCmd(USB_CTRL_PATH, "insert", FWUPDATE_USB_DEV_NAME, | 
|  | 849 | FWUPDATE_USB_VOL_IMG); | 
|  | 850 | } | 
|  | 851 | return ret; | 
|  | 852 | } | 
|  | 853 |  | 
|  | 854 | /* Eject the USB-mass-storage device status: success => 0, failure => non-0 */ | 
|  | 855 | static int usb_detach_device() | 
|  | 856 | { | 
|  | 857 | if (!usb_get_status()) | 
|  | 858 | { | 
|  | 859 | return 1; | 
|  | 860 | } | 
|  | 861 | return executeCmd(USB_CTRL_PATH, "eject", FWUPDATE_USB_DEV_NAME); | 
|  | 862 | } | 
|  | 863 |  | 
|  | 864 | constexpr uint8_t controls_init = 0x00; | 
|  | 865 | constexpr uint8_t controls_transfer_started = 0x01; | 
|  | 866 | constexpr uint8_t controls_transfer_completed = 0x02; | 
|  | 867 | constexpr uint8_t controls_transfer_aborted = 0x04; | 
|  | 868 | constexpr uint8_t controls_usb_attached = 0x08; | 
|  | 869 |  | 
|  | 870 | struct fw_update_control_request | 
|  | 871 | { | 
|  | 872 | enum knob | 
|  | 873 | { | 
|  | 874 | CTRL_GET = 0, | 
|  | 875 | CTRL_XFER_START, | 
|  | 876 | CTRL_XFER_COMPLETE, | 
|  | 877 | CTRL_XFER_ABORT, | 
|  | 878 | CTRL_SET_FILENAME, | 
|  | 879 | CTRL_USB_ATTACH, | 
|  | 880 | CTRL_USB_DETACH, | 
|  | 881 | } __attribute__((packed)); | 
|  | 882 | enum knob control; | 
|  | 883 | uint8_t nlen; | 
|  | 884 | char filename[fwPathMaxLength]; | 
|  | 885 | } __attribute__((packed)); | 
|  | 886 |  | 
|  | 887 | static ipmi_ret_t ipmi_firmware_control(ipmi_netfn_t netfn, ipmi_cmd_t cmd, | 
|  | 888 | ipmi_request_t request, | 
|  | 889 | ipmi_response_t response, | 
|  | 890 | ipmi_data_len_t data_len, | 
|  | 891 | ipmi_context_t context) | 
|  | 892 | { | 
|  | 893 | static std::string fw_xfer_uri; | 
|  | 894 |  | 
|  | 895 | if (DEBUG) | 
|  | 896 | std::cerr << "FW update control\n"; | 
|  | 897 | *data_len = 0; | 
|  | 898 |  | 
|  | 899 | static uint8_t controls = controls_init; | 
|  | 900 | ipmi_ret_t rc = IPMI_CC_OK; | 
|  | 901 | auto ctrl_req = reinterpret_cast<fw_update_control_request *>(request); | 
|  | 902 | auto ctrl_resp = reinterpret_cast<uint8_t *>(response); | 
|  | 903 |  | 
|  | 904 | if (usb_get_status()) | 
|  | 905 | { | 
|  | 906 | controls |= controls_usb_attached; | 
|  | 907 | } | 
|  | 908 | else | 
|  | 909 | { | 
|  | 910 | controls &= ~controls_usb_attached; | 
|  | 911 | } | 
|  | 912 |  | 
|  | 913 | switch (ctrl_req->control) | 
|  | 914 | { | 
|  | 915 | case fw_update_control_request::CTRL_GET: | 
|  | 916 | break; | 
|  | 917 | case fw_update_control_request::CTRL_XFER_START: | 
|  | 918 | { | 
|  | 919 | controls |= controls_transfer_started; | 
|  | 920 | // reset buffer to empty (truncate file) | 
|  | 921 | std::ofstream out(FIRMWARE_BUFFER_FILE, | 
|  | 922 | std::ofstream::binary | std::ofstream::trunc); | 
|  | 923 | fw_xfer_uri = std::string("file://") + FIRMWARE_BUFFER_FILE; | 
|  | 924 | if (xfer_hash_check) | 
|  | 925 | { | 
|  | 926 | xfer_hash_check->clear(); | 
|  | 927 | } | 
|  | 928 | if (DEBUG) | 
|  | 929 | std::cerr << "transfer start\n"; | 
|  | 930 | } | 
|  | 931 | break; | 
|  | 932 | case fw_update_control_request::CTRL_XFER_COMPLETE: | 
|  | 933 | { | 
|  | 934 | if (usb_get_status()) | 
|  | 935 | { | 
|  | 936 | rc = IPMI_CC_REQ_INVALID_PHASE; | 
|  | 937 | } | 
|  | 938 | // finish transfer based on URI | 
|  | 939 | if (!transfer_firmware_from_uri(fw_xfer_uri)) | 
|  | 940 | { | 
|  | 941 | rc = IPMI_CC_UNSPECIFIED_ERROR; | 
|  | 942 | break; | 
|  | 943 | } | 
|  | 944 | // transfer complete | 
|  | 945 | if (xfer_hash_check) | 
|  | 946 | { | 
|  | 947 | if (transfer_hash_check::CHECK_PASSED_SHA2 != | 
|  | 948 | xfer_hash_check->check()) | 
|  | 949 | { | 
|  | 950 | if (DEBUG) | 
|  | 951 | std::cerr << "xfer_hash_check returns not " | 
|  | 952 | "CHECK_PASSED_SHA2\n"; | 
|  | 953 | rc = IPMI_CC_UNSPECIFIED_ERROR; | 
|  | 954 | break; | 
|  | 955 | } | 
|  | 956 | } | 
|  | 957 | // start the request | 
|  | 958 | if (!request_start_firmware_update(FIRMWARE_BUFFER_FILE)) | 
|  | 959 | { | 
|  | 960 | if (DEBUG) | 
|  | 961 | std::cerr | 
|  | 962 | << "request_start_firmware_update returns failure\n"; | 
|  | 963 | rc = IPMI_CC_UNSPECIFIED_ERROR; | 
|  | 964 | } | 
|  | 965 | if (rc == IPMI_CC_OK) | 
|  | 966 | { | 
|  | 967 | controls |= controls_transfer_completed; | 
|  | 968 | } | 
|  | 969 | } | 
|  | 970 | break; | 
|  | 971 | case fw_update_control_request::CTRL_XFER_ABORT: | 
|  | 972 | if (DEBUG) | 
|  | 973 | std::cerr << "send abort request\n"; | 
|  | 974 | if (usb_get_status()) | 
|  | 975 | { | 
|  | 976 | if (0 != usb_detach_device()) | 
|  | 977 | { | 
|  | 978 | rc = IPMI_CC_USB_ATTACH_FAIL; | 
|  | 979 | } | 
|  | 980 | } | 
|  | 981 | if (!request_abort_firmware_update()) | 
|  | 982 | { | 
|  | 983 | if (DEBUG) | 
|  | 984 | std::cerr | 
|  | 985 | << "abort_start_firmware_update returns failure\n"; | 
|  | 986 | rc = IPMI_CC_UNSPECIFIED_ERROR; | 
|  | 987 | } | 
|  | 988 | controls |= controls_transfer_aborted; | 
|  | 989 | break; | 
|  | 990 | case fw_update_control_request::CTRL_SET_FILENAME: | 
|  | 991 | fw_xfer_uri.clear(); | 
|  | 992 | fw_xfer_uri.insert(0, ctrl_req->filename, ctrl_req->nlen); | 
|  | 993 | break; | 
|  | 994 | case fw_update_control_request::CTRL_USB_ATTACH: | 
|  | 995 | if (usb_get_status()) | 
|  | 996 | { | 
|  | 997 | rc = IPMI_CC_INVALID_FIELD_REQUEST; | 
|  | 998 | } | 
|  | 999 | else if (0 != usb_attach_device()) | 
|  | 1000 | { | 
|  | 1001 | rc = IPMI_CC_USB_ATTACH_FAIL; | 
|  | 1002 | } | 
|  | 1003 | else | 
|  | 1004 | { | 
|  | 1005 | rc = IPMI_CC_OK; | 
|  | 1006 | } | 
|  | 1007 | break; | 
|  | 1008 | case fw_update_control_request::CTRL_USB_DETACH: | 
|  | 1009 | if (!usb_get_status()) | 
|  | 1010 | { | 
|  | 1011 | rc = IPMI_CC_INVALID_FIELD_REQUEST; | 
|  | 1012 | } | 
|  | 1013 | if (0 != usb_detach_device()) | 
|  | 1014 | { | 
|  | 1015 | rc = IPMI_CC_USB_ATTACH_FAIL; | 
|  | 1016 | } | 
|  | 1017 | else | 
|  | 1018 | { | 
|  | 1019 | rc = IPMI_CC_OK; | 
|  | 1020 | } | 
|  | 1021 | break; | 
|  | 1022 | default: | 
|  | 1023 | if (DEBUG) | 
|  | 1024 | std::cerr << "control byte " << std::hex << ctrl_req->control | 
|  | 1025 | << " unknown\n"; | 
|  | 1026 | rc = IPMI_CC_INVALID_FIELD_REQUEST; | 
|  | 1027 | break; | 
|  | 1028 | } | 
|  | 1029 |  | 
|  | 1030 | if (rc == IPMI_CC_OK) | 
|  | 1031 | { | 
|  | 1032 | *ctrl_resp = controls; | 
|  | 1033 | *data_len = sizeof(*ctrl_resp); | 
|  | 1034 | } | 
|  | 1035 |  | 
|  | 1036 | return rc; | 
|  | 1037 | } | 
|  | 1038 |  | 
|  | 1039 | struct fw_version_info | 
|  | 1040 | { | 
|  | 1041 | uint8_t id_tag; | 
|  | 1042 | uint8_t major; | 
|  | 1043 | uint8_t minor; | 
|  | 1044 | uint32_t build; | 
|  | 1045 | uint32_t build_time; | 
|  | 1046 | uint32_t update_time; | 
|  | 1047 | } __attribute__((packed)); | 
|  | 1048 |  | 
|  | 1049 | static ipmi_ret_t ipmi_firmware_get_fw_version_info( | 
|  | 1050 | ipmi_netfn_t netfn, ipmi_cmd_t cmd, ipmi_request_t request, | 
|  | 1051 | ipmi_response_t response, ipmi_data_len_t data_len, ipmi_context_t context) | 
|  | 1052 | { | 
|  | 1053 | if (DEBUG) | 
|  | 1054 | std::cerr << "Get FW Version Info\n"; | 
|  | 1055 |  | 
|  | 1056 | // Byte 1 - Count (N) Number of devices data is being returned for. | 
|  | 1057 | // Byte 2 - ID Tag 00 – reserved 01 – BMC Active Image 02 – BBU Active Image | 
|  | 1058 | //                 03 – BMC Backup Image 04 – BBU Backup Image 05 – BBR | 
|  | 1059 | //                 Image | 
|  | 1060 | // Byte 3 - Major Version Number | 
|  | 1061 | // Byte 4 - Minor Version Number | 
|  | 1062 | // Bytes 5:8 - Build Number | 
|  | 1063 | // Bytes 9:12 - Build Timestamp Format: LSB first, same format as SEL | 
|  | 1064 | // timestamp | 
|  | 1065 | // Bytes 13:16 - Update Timestamp | 
|  | 1066 | // Bytes - 17:(15xN) - Repeat of 2 through 16 | 
|  | 1067 |  | 
|  | 1068 | uint8_t count = 0; | 
|  | 1069 | auto ret_count = reinterpret_cast<uint8_t *>(response); | 
|  | 1070 | auto info = reinterpret_cast<struct fw_version_info *>(ret_count + 1); | 
|  | 1071 |  | 
|  | 1072 | for (uint8_t id_tag = 1; id_tag < 6; id_tag++) | 
|  | 1073 | { | 
|  | 1074 | const char *fw_path; | 
|  | 1075 | switch (id_tag) | 
|  | 1076 | { | 
|  | 1077 | case 1: | 
|  | 1078 | fw_path = FW_UPDATE_ACTIVE_INFO_PATH; | 
|  | 1079 | break; | 
|  | 1080 | case 2: | 
|  | 1081 | fw_path = FW_UPDATE_BACKUP_INFO_PATH; | 
|  | 1082 | break; | 
|  | 1083 | case 3: | 
|  | 1084 | case 4: | 
|  | 1085 | case 5: | 
|  | 1086 | continue; // skip for now | 
|  | 1087 | break; | 
|  | 1088 | } | 
| Vernon Mauery | 15419dd | 2019-05-24 09:40:30 -0700 | [diff] [blame] | 1089 | std::shared_ptr<sdbusplus::asio::connection> bus = getSdBus(); | 
| Vernon Mauery | 52ce662 | 2019-05-22 09:19:46 -0700 | [diff] [blame] | 1090 | auto method = | 
| Vernon Mauery | 15419dd | 2019-05-24 09:40:30 -0700 | [diff] [blame] | 1091 | bus->new_method_call(FW_UPDATE_SERVER_DBUS_NAME, fw_path, | 
| Vernon Mauery | 52ce662 | 2019-05-22 09:19:46 -0700 | [diff] [blame] | 1092 | "org.freedesktop.DBus.Properties", "GetAll"); | 
|  | 1093 | method.append(FW_UPDATE_INFO_INTERFACE); | 
|  | 1094 | std::vector<std::pair<std::string, ipmi::DbusVariant>> properties; | 
|  | 1095 | try | 
|  | 1096 | { | 
| Vernon Mauery | 15419dd | 2019-05-24 09:40:30 -0700 | [diff] [blame] | 1097 | auto reply = bus->call(method); | 
| Vernon Mauery | 52ce662 | 2019-05-22 09:19:46 -0700 | [diff] [blame] | 1098 |  | 
|  | 1099 | if (reply.is_method_error()) | 
|  | 1100 | continue; | 
|  | 1101 |  | 
|  | 1102 | reply.read(properties); | 
|  | 1103 | } | 
|  | 1104 | catch (sdbusplus::exception::SdBusError &e) | 
|  | 1105 | { | 
|  | 1106 | std::cerr << "SDBus Error: " << e.what(); | 
|  | 1107 | return IPMI_CC_UNSPECIFIED_ERROR; | 
|  | 1108 | } | 
|  | 1109 | uint8_t major = 0; | 
|  | 1110 | uint8_t minor = 0; | 
|  | 1111 | uint32_t build = 0; | 
|  | 1112 | int32_t build_time = 0; | 
|  | 1113 | int32_t update_time = 0; | 
|  | 1114 | for (const auto &t : properties) | 
|  | 1115 | { | 
|  | 1116 | auto key = t.first; | 
|  | 1117 | auto value = t.second; | 
|  | 1118 | if (key == "version") | 
|  | 1119 | { | 
| Vernon Mauery | 8166c8d | 2019-05-23 11:22:30 -0700 | [diff] [blame] | 1120 | auto strver = std::get<std::string>(value); | 
| Vernon Mauery | 52ce662 | 2019-05-22 09:19:46 -0700 | [diff] [blame] | 1121 | std::stringstream ss; | 
|  | 1122 | ss << std::hex << strver; | 
|  | 1123 | uint32_t t; | 
|  | 1124 | ss >> t; | 
|  | 1125 | major = t; | 
|  | 1126 | ss.ignore(); | 
|  | 1127 | ss >> t; | 
|  | 1128 | minor = t; | 
|  | 1129 | ss.ignore(); | 
|  | 1130 | ss >> build; | 
|  | 1131 | } | 
|  | 1132 | else if (key == "build_time") | 
|  | 1133 | { | 
| Vernon Mauery | 8166c8d | 2019-05-23 11:22:30 -0700 | [diff] [blame] | 1134 | build_time = std::get<int32_t>(value); | 
| Vernon Mauery | 52ce662 | 2019-05-22 09:19:46 -0700 | [diff] [blame] | 1135 | } | 
|  | 1136 | else if (key == "update_time") | 
|  | 1137 | { | 
| Vernon Mauery | 8166c8d | 2019-05-23 11:22:30 -0700 | [diff] [blame] | 1138 | update_time = std::get<int32_t>(value); | 
| Vernon Mauery | 52ce662 | 2019-05-22 09:19:46 -0700 | [diff] [blame] | 1139 | } | 
|  | 1140 | } | 
|  | 1141 |  | 
|  | 1142 | info->id_tag = id_tag; | 
|  | 1143 | info->major = major; | 
|  | 1144 | info->minor = minor; | 
|  | 1145 | info->build = build; | 
|  | 1146 | info->build_time = build_time; | 
|  | 1147 | info->update_time = update_time; | 
|  | 1148 | count++; | 
|  | 1149 | info++; | 
|  | 1150 | } | 
|  | 1151 | *ret_count = count; | 
|  | 1152 |  | 
|  | 1153 | // Status code. | 
|  | 1154 | ipmi_ret_t rc = IPMI_CC_OK; | 
|  | 1155 | *data_len = sizeof(count) + count * sizeof(*info); | 
|  | 1156 |  | 
|  | 1157 | return rc; | 
|  | 1158 | } | 
|  | 1159 |  | 
|  | 1160 | struct fw_security_revision_info | 
|  | 1161 | { | 
|  | 1162 | uint8_t id_tag; | 
|  | 1163 | uint16_t sec_rev; | 
|  | 1164 | } __attribute__((packed)); | 
|  | 1165 |  | 
|  | 1166 | static ipmi_ret_t ipmi_firmware_get_fw_security_revision( | 
|  | 1167 | ipmi_netfn_t netfn, ipmi_cmd_t cmd, ipmi_request_t request, | 
|  | 1168 | ipmi_response_t response, ipmi_data_len_t data_len, ipmi_context_t context) | 
|  | 1169 | { | 
|  | 1170 | if (DEBUG) | 
|  | 1171 | std::cerr << "Get FW security revision info\n"; | 
|  | 1172 |  | 
|  | 1173 | // Byte 1 - Count (N) Number of devices data is being returned for. | 
|  | 1174 | // Byte 2 - ID Tag 00 – reserved 01 – BMC Active Image 02 – BBU Active Image | 
|  | 1175 | //                 03 – BMC Backup Image 04 – BBU Backup Image 05 – BBR | 
|  | 1176 | //                 Image | 
|  | 1177 | // Byte 3 - Major Version Number | 
|  | 1178 | // Byte 4 - Minor Version Number | 
|  | 1179 | // Bytes 5:8 - Build Number | 
|  | 1180 | // Bytes 9:12 - Build Timestamp Format: LSB first, same format as SEL | 
|  | 1181 | // timestamp | 
|  | 1182 | // Bytes 13:16 - Update Timestamp | 
|  | 1183 | // Bytes - 17:(15xN) - Repeat of 2 through 16 | 
|  | 1184 |  | 
|  | 1185 | uint8_t count = 0; | 
|  | 1186 | auto ret_count = reinterpret_cast<uint8_t *>(response); | 
|  | 1187 | auto info = | 
|  | 1188 | reinterpret_cast<struct fw_security_revision_info *>(ret_count + 1); | 
|  | 1189 |  | 
| Vernon Mauery | 15419dd | 2019-05-24 09:40:30 -0700 | [diff] [blame] | 1190 | std::shared_ptr<sdbusplus::asio::connection> bus = getSdBus(); | 
| Vernon Mauery | 52ce662 | 2019-05-22 09:19:46 -0700 | [diff] [blame] | 1191 | for (uint8_t id_tag = 1; id_tag < 6; id_tag++) | 
|  | 1192 | { | 
|  | 1193 | const char *fw_path; | 
|  | 1194 | switch (id_tag) | 
|  | 1195 | { | 
|  | 1196 | case 1: | 
|  | 1197 | fw_path = FW_UPDATE_ACTIVE_INFO_PATH; | 
|  | 1198 | break; | 
|  | 1199 | case 2: | 
|  | 1200 | fw_path = FW_UPDATE_BACKUP_INFO_PATH; | 
|  | 1201 | break; | 
|  | 1202 | case 3: | 
|  | 1203 | case 4: | 
|  | 1204 | case 5: | 
|  | 1205 | continue; // skip for now | 
|  | 1206 | break; | 
|  | 1207 | } | 
|  | 1208 | auto method = | 
| Vernon Mauery | 15419dd | 2019-05-24 09:40:30 -0700 | [diff] [blame] | 1209 | bus->new_method_call(FW_UPDATE_SERVER_DBUS_NAME, fw_path, | 
| Vernon Mauery | 52ce662 | 2019-05-22 09:19:46 -0700 | [diff] [blame] | 1210 | "org.freedesktop.DBus.Properties", "GetAll"); | 
|  | 1211 | method.append(FW_UPDATE_INFO_INTERFACE, "security_version"); | 
|  | 1212 | ipmi::DbusVariant sec_rev; | 
|  | 1213 | try | 
|  | 1214 | { | 
| Vernon Mauery | 15419dd | 2019-05-24 09:40:30 -0700 | [diff] [blame] | 1215 | auto reply = bus->call(method); | 
| Vernon Mauery | 52ce662 | 2019-05-22 09:19:46 -0700 | [diff] [blame] | 1216 |  | 
|  | 1217 | if (reply.is_method_error()) | 
|  | 1218 | continue; | 
|  | 1219 |  | 
|  | 1220 | reply.read(sec_rev); | 
|  | 1221 | } | 
|  | 1222 | catch (sdbusplus::exception::SdBusError &e) | 
|  | 1223 | { | 
|  | 1224 | std::cerr << "SDBus Error: " << e.what(); | 
|  | 1225 | return IPMI_CC_UNSPECIFIED_ERROR; | 
|  | 1226 | } | 
|  | 1227 |  | 
|  | 1228 | info->id_tag = id_tag; | 
| Vernon Mauery | 8166c8d | 2019-05-23 11:22:30 -0700 | [diff] [blame] | 1229 | info->sec_rev = std::get<int>(sec_rev); | 
| Vernon Mauery | 52ce662 | 2019-05-22 09:19:46 -0700 | [diff] [blame] | 1230 | count++; | 
|  | 1231 | info++; | 
|  | 1232 | } | 
|  | 1233 | *ret_count = count; | 
|  | 1234 |  | 
|  | 1235 | // Status code. | 
|  | 1236 | ipmi_ret_t rc = IPMI_CC_OK; | 
|  | 1237 | *data_len = sizeof(count) + count * sizeof(*info); | 
|  | 1238 |  | 
|  | 1239 | return rc; | 
|  | 1240 | } | 
|  | 1241 |  | 
|  | 1242 | struct fw_channel_size | 
|  | 1243 | { | 
|  | 1244 | uint8_t channel_id; | 
|  | 1245 | uint32_t channel_size; | 
|  | 1246 | } __attribute__((packed)); | 
|  | 1247 |  | 
|  | 1248 | enum | 
|  | 1249 | { | 
|  | 1250 | CHANNEL_RESVD = 0, | 
|  | 1251 | CHANNEL_KCS, | 
|  | 1252 | CHANNEL_RMCP_PLUS, | 
|  | 1253 | CHANNEL_USB_DATA, | 
|  | 1254 | CHANNEL_USB_MASS_STORAGE, | 
|  | 1255 | } channel_transfer_type; | 
|  | 1256 |  | 
|  | 1257 | static ipmi_ret_t ipmi_firmware_max_transfer_size( | 
|  | 1258 | ipmi_netfn_t netfn, ipmi_cmd_t cmd, ipmi_request_t request, | 
|  | 1259 | ipmi_response_t response, ipmi_data_len_t data_len, ipmi_context_t context) | 
|  | 1260 | { | 
|  | 1261 | if (DEBUG) | 
|  | 1262 | std::cerr << "Get FW max transfer size\n"; | 
|  | 1263 |  | 
|  | 1264 | // Byte 1 - Count (N) Number of devices data is being returned for. | 
|  | 1265 | // Byte 2 - ID Tag 00 – reserved 01 – kcs 02 – rmcp+, | 
|  | 1266 | //                 03 – usb data, 04 – usb mass storage | 
|  | 1267 | // Byte 3-6 - transfer size (little endian) | 
|  | 1268 | // Bytes - 7:(5xN) - Repeat of 2 through 6 | 
|  | 1269 |  | 
|  | 1270 | uint8_t count = 0; | 
|  | 1271 | auto ret_count = reinterpret_cast<uint8_t *>(response); | 
|  | 1272 | auto info = reinterpret_cast<struct fw_channel_size *>(ret_count + 1); | 
|  | 1273 |  | 
|  | 1274 | info->channel_id = CHANNEL_KCS; | 
|  | 1275 | info->channel_size = 128; | 
|  | 1276 | info++; | 
|  | 1277 | count++; | 
|  | 1278 |  | 
|  | 1279 | info->channel_id = CHANNEL_RMCP_PLUS; | 
|  | 1280 | info->channel_size = 50 * 1024; | 
|  | 1281 | info++; | 
|  | 1282 | count++; | 
|  | 1283 |  | 
|  | 1284 | /* | 
|  | 1285 | info->channel_id = CHANNEL_USB_MASS_STORAGE; | 
|  | 1286 | info->channel_size = 128; | 
|  | 1287 | info++; | 
|  | 1288 | count++; | 
|  | 1289 | */ | 
|  | 1290 |  | 
|  | 1291 | *ret_count = count; | 
|  | 1292 |  | 
|  | 1293 | // Status code. | 
|  | 1294 | ipmi_ret_t rc = IPMI_CC_OK; | 
|  | 1295 | *data_len = sizeof(count) + count * sizeof(*info); | 
|  | 1296 |  | 
|  | 1297 | return rc; | 
|  | 1298 | } | 
|  | 1299 |  | 
|  | 1300 | enum | 
|  | 1301 | { | 
|  | 1302 | EXEC_CTX_RESVD = 0, | 
|  | 1303 | EXEC_CTX_FULL_LINUX = 0x10, | 
|  | 1304 | EXEC_CTX_SAFE_MODE_LINUX = 0x11, | 
|  | 1305 | } bmc_execution_context; | 
|  | 1306 |  | 
|  | 1307 | struct fw_execution_context | 
|  | 1308 | { | 
|  | 1309 | uint8_t context; | 
|  | 1310 | uint8_t image_selection; | 
|  | 1311 | } __attribute__((packed)); | 
|  | 1312 |  | 
|  | 1313 | static ipmi_ret_t ipmi_firmware_get_fw_execution_context( | 
|  | 1314 | ipmi_netfn_t netfn, ipmi_cmd_t cmd, ipmi_request_t request, | 
|  | 1315 | ipmi_response_t response, ipmi_data_len_t data_len, ipmi_context_t context) | 
|  | 1316 | { | 
|  | 1317 | if (DEBUG) | 
|  | 1318 | std::cerr << "Get FW execution context\n"; | 
|  | 1319 |  | 
|  | 1320 | // Byte 1 - execution context | 
|  | 1321 | //          0x10 - full linux stack, 0x11 - safe-mode linux stack | 
|  | 1322 | // Byte 2 - current image selection | 
|  | 1323 | //          1 - primary, 2 - secondary | 
|  | 1324 |  | 
|  | 1325 | auto info = reinterpret_cast<struct fw_execution_context *>(response); | 
| Vernon Mauery | 15419dd | 2019-05-24 09:40:30 -0700 | [diff] [blame] | 1326 | std::shared_ptr<sdbusplus::asio::connection> bus = getSdBus(); | 
| Vernon Mauery | 52ce662 | 2019-05-22 09:19:46 -0700 | [diff] [blame] | 1327 | auto method = | 
| Vernon Mauery | 15419dd | 2019-05-24 09:40:30 -0700 | [diff] [blame] | 1328 | bus->new_method_call(FW_UPDATE_SERVER_DBUS_NAME, FW_UPDATE_SERVER_PATH, | 
| Vernon Mauery | 52ce662 | 2019-05-22 09:19:46 -0700 | [diff] [blame] | 1329 | "org.freedesktop.DBus.Properties", "GetAll"); | 
|  | 1330 | method.append(FW_UPDATE_SECURITY_INTERFACE); | 
|  | 1331 | int active_img; | 
|  | 1332 | bool safe_mode; | 
|  | 1333 | std::string cert; | 
|  | 1334 | try | 
|  | 1335 | { | 
| Vernon Mauery | 15419dd | 2019-05-24 09:40:30 -0700 | [diff] [blame] | 1336 | auto reply = bus->call(method); | 
| Vernon Mauery | 52ce662 | 2019-05-22 09:19:46 -0700 | [diff] [blame] | 1337 |  | 
|  | 1338 | if (!reply.is_method_error()) | 
|  | 1339 | { | 
|  | 1340 | std::vector<std::pair<std::string, ipmi::DbusVariant>> properties; | 
|  | 1341 | reply.read(properties); | 
|  | 1342 |  | 
|  | 1343 | for (const auto &t : properties) | 
|  | 1344 | { | 
|  | 1345 | auto key = t.first; | 
|  | 1346 | auto value = t.second; | 
|  | 1347 | if (key == "active_partition") | 
|  | 1348 | { | 
| Vernon Mauery | 8166c8d | 2019-05-23 11:22:30 -0700 | [diff] [blame] | 1349 | active_img = std::get<int>(value); | 
| Vernon Mauery | 52ce662 | 2019-05-22 09:19:46 -0700 | [diff] [blame] | 1350 | } | 
|  | 1351 | else if (key == "safe_mode") | 
|  | 1352 | { | 
| Vernon Mauery | 8166c8d | 2019-05-23 11:22:30 -0700 | [diff] [blame] | 1353 | safe_mode = std::get<bool>(value); | 
| Vernon Mauery | 52ce662 | 2019-05-22 09:19:46 -0700 | [diff] [blame] | 1354 | } | 
|  | 1355 | } | 
|  | 1356 | } | 
|  | 1357 | } | 
|  | 1358 | catch (sdbusplus::exception::SdBusError &e) | 
|  | 1359 | { | 
|  | 1360 | std::cerr << "SDBus Error: " << e.what(); | 
|  | 1361 | return IPMI_CC_UNSPECIFIED_ERROR; | 
|  | 1362 | } | 
|  | 1363 |  | 
|  | 1364 | if (safe_mode) | 
|  | 1365 | info->context = EXEC_CTX_SAFE_MODE_LINUX; | 
|  | 1366 | else | 
|  | 1367 | info->context = EXEC_CTX_FULL_LINUX; | 
|  | 1368 |  | 
|  | 1369 | info->image_selection = active_img; | 
|  | 1370 |  | 
|  | 1371 | // Status code. | 
|  | 1372 | ipmi_ret_t rc = IPMI_CC_OK; | 
|  | 1373 | *data_len = sizeof(*info); | 
|  | 1374 |  | 
|  | 1375 | return rc; | 
|  | 1376 | } | 
|  | 1377 |  | 
|  | 1378 | struct fw_update_status_response | 
|  | 1379 | { | 
|  | 1380 | uint8_t status; | 
|  | 1381 | uint8_t percent; | 
|  | 1382 | uint8_t check; | 
|  | 1383 | } __attribute__((packed)); | 
|  | 1384 |  | 
|  | 1385 | static ipmi_ret_t ipmi_firmware_get_status(ipmi_netfn_t netfn, ipmi_cmd_t cmd, | 
|  | 1386 | ipmi_request_t request, | 
|  | 1387 | ipmi_response_t response, | 
|  | 1388 | ipmi_data_len_t data_len, | 
|  | 1389 | ipmi_context_t context) | 
|  | 1390 | { | 
|  | 1391 | if (DEBUG) | 
|  | 1392 | std::cerr << "Get FW update status\n"; | 
|  | 1393 |  | 
|  | 1394 | // Byte 1 - status (0=init, 1=idle, 2=download, 3=validate, 4=write, | 
|  | 1395 | //                  5=ready, f=error, 83=ac cycle required) | 
|  | 1396 | // Byte 2 - percent | 
|  | 1397 | // Byte 3 - integrity check status (0=none, 1=req, 2=sha2ok, e2=sha2fail) | 
|  | 1398 |  | 
|  | 1399 | auto fw_status = | 
|  | 1400 | reinterpret_cast<struct fw_update_status_response *>(response); | 
|  | 1401 |  | 
|  | 1402 | fw_status->status = fw_update_status.state(); | 
|  | 1403 | fw_status->percent = fw_update_status.percent(); | 
|  | 1404 | fw_status->check = xfer_hash_check ? xfer_hash_check->status() : 0; | 
|  | 1405 |  | 
|  | 1406 | // Status code. | 
|  | 1407 | *data_len = sizeof(*fw_status); | 
|  | 1408 | return IPMI_CC_OK; | 
|  | 1409 | } | 
|  | 1410 |  | 
|  | 1411 | static constexpr uint8_t FW_UPDATE_OPTIONS_NO_DOWNREV = (1 << 0); | 
|  | 1412 | static constexpr uint8_t FW_UPDATE_OPTIONS_DEFER_RESTART = (1 << 1); | 
|  | 1413 | static constexpr uint8_t FW_UPDATE_OPTIONS_SHA2_CHECK = (1 << 2); | 
|  | 1414 | static constexpr uint8_t FW_UPDATE_OPTIONS_RESVD1 = (1 << 3); | 
|  | 1415 | struct fw_update_options_request | 
|  | 1416 | { | 
|  | 1417 | uint8_t mask; | 
|  | 1418 | uint8_t options; | 
|  | 1419 | } __attribute__((packed)); | 
|  | 1420 |  | 
|  | 1421 | bool fw_update_set_dbus_property(const std::string &path, | 
|  | 1422 | const std::string &iface, | 
|  | 1423 | const std::string &name, | 
|  | 1424 | ipmi::DbusVariant value) | 
|  | 1425 | { | 
| Vernon Mauery | 15419dd | 2019-05-24 09:40:30 -0700 | [diff] [blame] | 1426 | std::shared_ptr<sdbusplus::asio::connection> bus = getSdBus(); | 
| Vernon Mauery | 52ce662 | 2019-05-22 09:19:46 -0700 | [diff] [blame] | 1427 | auto method = | 
| Vernon Mauery | 15419dd | 2019-05-24 09:40:30 -0700 | [diff] [blame] | 1428 | bus->new_method_call(FW_UPDATE_SERVER_DBUS_NAME, path.c_str(), | 
| Vernon Mauery | 52ce662 | 2019-05-22 09:19:46 -0700 | [diff] [blame] | 1429 | "org.freedesktop.DBus.Properties", "Set"); | 
|  | 1430 | method.append(iface, name, value); | 
|  | 1431 | try | 
|  | 1432 | { | 
| Vernon Mauery | 15419dd | 2019-05-24 09:40:30 -0700 | [diff] [blame] | 1433 | auto reply = bus->call(method); | 
| Vernon Mauery | 52ce662 | 2019-05-22 09:19:46 -0700 | [diff] [blame] | 1434 | auto err = reply.is_method_error(); | 
|  | 1435 | if (err) | 
|  | 1436 | if (DEBUG) | 
|  | 1437 | std::cerr << "failed to set prop " << path << '/' << iface | 
|  | 1438 | << '.' << name << '\n'; | 
|  | 1439 | return err; | 
|  | 1440 | } | 
|  | 1441 | catch (sdbusplus::exception::SdBusError &e) | 
|  | 1442 | { | 
|  | 1443 | std::cerr << "SDBus Error: " << e.what(); | 
|  | 1444 | return false; | 
|  | 1445 | } | 
|  | 1446 | } | 
|  | 1447 |  | 
|  | 1448 | uint32_t fw_update_options = 0; | 
|  | 1449 | static ipmi_ret_t ipmi_firmware_update_options( | 
|  | 1450 | ipmi_netfn_t netfn, ipmi_cmd_t cmd, ipmi_request_t request, | 
|  | 1451 | ipmi_response_t response, ipmi_data_len_t data_len, ipmi_context_t context) | 
|  | 1452 | { | 
|  | 1453 | if (DEBUG) | 
|  | 1454 | std::cerr << "Get/set FW update options\n"; | 
|  | 1455 |  | 
|  | 1456 | // request: | 
|  | 1457 | // Byte 1 - mask | 
|  | 1458 | // Byte 2 - options | 
|  | 1459 | // Byte 3-34 - optional integrity check expected value | 
|  | 1460 | // response: | 
|  | 1461 | // Byte 1 - set options | 
|  | 1462 |  | 
|  | 1463 | auto fw_options = | 
|  | 1464 | reinterpret_cast<struct fw_update_options_request *>(request); | 
|  | 1465 |  | 
|  | 1466 | const char *path = FW_UPDATE_SERVER_INFO_PATH; | 
|  | 1467 | const char *iface = FW_UPDATE_SECURITY_INTERFACE; | 
|  | 1468 | if ((fw_options->mask & FW_UPDATE_OPTIONS_NO_DOWNREV) && | 
|  | 1469 | (fw_options->options & FW_UPDATE_OPTIONS_NO_DOWNREV) != | 
|  | 1470 | (fw_update_options & FW_UPDATE_OPTIONS_NO_DOWNREV)) | 
|  | 1471 | { | 
|  | 1472 | if (fw_options->options & FW_UPDATE_OPTIONS_NO_DOWNREV) | 
|  | 1473 | { | 
|  | 1474 | fw_update_options |= FW_UPDATE_OPTIONS_NO_DOWNREV; | 
|  | 1475 | } | 
|  | 1476 | else | 
|  | 1477 | { | 
|  | 1478 | fw_update_options &= ~FW_UPDATE_OPTIONS_NO_DOWNREV; | 
|  | 1479 | // send dbus | 
|  | 1480 | } | 
|  | 1481 | const char *name = "inhibit_downgrade"; | 
|  | 1482 | fw_update_set_dbus_property( | 
|  | 1483 | path, iface, name, | 
|  | 1484 | (bool)(!!(fw_update_options & FW_UPDATE_OPTIONS_NO_DOWNREV))); | 
|  | 1485 | } | 
|  | 1486 | if ((fw_options->mask & FW_UPDATE_OPTIONS_DEFER_RESTART) && | 
|  | 1487 | (fw_options->options & FW_UPDATE_OPTIONS_DEFER_RESTART) != | 
|  | 1488 | (fw_update_options & FW_UPDATE_OPTIONS_DEFER_RESTART)) | 
|  | 1489 | { | 
|  | 1490 | if (fw_options->options & FW_UPDATE_OPTIONS_DEFER_RESTART) | 
|  | 1491 | { | 
|  | 1492 | fw_update_options |= FW_UPDATE_OPTIONS_DEFER_RESTART; | 
|  | 1493 | } | 
|  | 1494 | else | 
|  | 1495 | { | 
|  | 1496 | fw_update_options &= ~FW_UPDATE_OPTIONS_DEFER_RESTART; | 
|  | 1497 | } | 
|  | 1498 | const char *name = "defer_restart"; | 
|  | 1499 | fw_update_set_dbus_property( | 
|  | 1500 | path, iface, name, | 
|  | 1501 | (bool)(!!(fw_update_options & FW_UPDATE_OPTIONS_DEFER_RESTART))); | 
|  | 1502 | } | 
|  | 1503 | if (fw_options->mask & FW_UPDATE_OPTIONS_SHA2_CHECK) | 
|  | 1504 | { | 
|  | 1505 | auto hash_size = EVP_MD_size(EVP_sha256()); | 
|  | 1506 | if (fw_options->options & FW_UPDATE_OPTIONS_SHA2_CHECK) | 
|  | 1507 | { | 
|  | 1508 | if (*data_len != (sizeof(*fw_options) + hash_size)) | 
|  | 1509 | { | 
|  | 1510 | *data_len = 0; | 
|  | 1511 | return IPMI_CC_REQ_DATA_LEN_INVALID; | 
|  | 1512 | } | 
|  | 1513 | xfer_hash_check = std::make_shared<transfer_hash_check>(); | 
|  | 1514 | auto exp_hash = reinterpret_cast<uint8_t *>(fw_options + 1); | 
|  | 1515 | xfer_hash_check->init({exp_hash, exp_hash + hash_size}); | 
|  | 1516 | fw_update_options |= FW_UPDATE_OPTIONS_SHA2_CHECK; | 
|  | 1517 | } | 
|  | 1518 | else | 
|  | 1519 | { | 
|  | 1520 | fw_update_options &= ~FW_UPDATE_OPTIONS_SHA2_CHECK; | 
|  | 1521 | // delete the xfer_hash_check object | 
|  | 1522 | xfer_hash_check.reset(); | 
|  | 1523 | } | 
|  | 1524 | } | 
|  | 1525 | auto options_rsp = reinterpret_cast<uint8_t *>(response); | 
|  | 1526 | *options_rsp = fw_update_options; | 
|  | 1527 |  | 
|  | 1528 | if (DEBUG) | 
|  | 1529 | std::cerr << "current fw_update_options = " << std::hex | 
|  | 1530 | << fw_update_options << '\n'; | 
|  | 1531 | // Status code. | 
|  | 1532 | *data_len = sizeof(*options_rsp); | 
|  | 1533 | return IPMI_CC_OK; | 
|  | 1534 | } | 
|  | 1535 |  | 
|  | 1536 | struct fw_cert_info | 
|  | 1537 | { | 
|  | 1538 | uint16_t cert_len; | 
|  | 1539 | uint64_t serial; | 
|  | 1540 | uint8_t subject_len; | 
|  | 1541 | char subject[255]; | 
|  | 1542 | } __attribute__((packed)); | 
|  | 1543 |  | 
|  | 1544 | static ipmi_ret_t ipmi_firmware_get_root_cert_info( | 
|  | 1545 | ipmi_netfn_t netfn, ipmi_cmd_t cmd, ipmi_request_t request, | 
|  | 1546 | ipmi_response_t response, ipmi_data_len_t data_len, ipmi_context_t context) | 
|  | 1547 | { | 
|  | 1548 | if (DEBUG) | 
|  | 1549 | std::cerr << "Get FW root cert info\n"; | 
|  | 1550 |  | 
|  | 1551 | // request: | 
|  | 1552 | // Byte 1 - certificate ID: request which certificate (ignored) | 
|  | 1553 |  | 
|  | 1554 | // response: | 
|  | 1555 | // Byte 1-2  - certificate length (little endian) | 
|  | 1556 | // Byte 3-10 - serial number (little endian) | 
|  | 1557 | // Byte 11   - subject length | 
|  | 1558 | // Byte 12-N - subject data | 
|  | 1559 |  | 
|  | 1560 | auto cert_info = reinterpret_cast<struct fw_cert_info *>(response); | 
| Vernon Mauery | 15419dd | 2019-05-24 09:40:30 -0700 | [diff] [blame] | 1561 | std::shared_ptr<sdbusplus::asio::connection> bus = getSdBus(); | 
|  | 1562 | auto method = bus->new_method_call( | 
| Vernon Mauery | 52ce662 | 2019-05-22 09:19:46 -0700 | [diff] [blame] | 1563 | FW_UPDATE_SERVER_DBUS_NAME, FW_UPDATE_SERVER_INFO_PATH, | 
|  | 1564 | "org.freedesktop.DBus.Properties", "GetAll"); | 
|  | 1565 | method.append(FW_UPDATE_SECURITY_INTERFACE); | 
|  | 1566 | std::string subject; | 
|  | 1567 | uint64_t serial; | 
|  | 1568 | std::string cert; | 
|  | 1569 | try | 
|  | 1570 | { | 
| Vernon Mauery | 15419dd | 2019-05-24 09:40:30 -0700 | [diff] [blame] | 1571 | auto reply = bus->call(method); | 
| Vernon Mauery | 52ce662 | 2019-05-22 09:19:46 -0700 | [diff] [blame] | 1572 |  | 
|  | 1573 | std::vector<std::pair<std::string, ipmi::DbusVariant>> properties; | 
|  | 1574 | reply.read(properties); | 
|  | 1575 |  | 
|  | 1576 | for (const auto &t : properties) | 
|  | 1577 | { | 
|  | 1578 | auto key = t.first; | 
|  | 1579 | auto value = t.second; | 
|  | 1580 | if (key == "certificate_subject") | 
|  | 1581 | { | 
| Vernon Mauery | 8166c8d | 2019-05-23 11:22:30 -0700 | [diff] [blame] | 1582 | subject = std::get<std::string>(value); | 
| Vernon Mauery | 52ce662 | 2019-05-22 09:19:46 -0700 | [diff] [blame] | 1583 | } | 
|  | 1584 | else if (key == "cetificate_serial") | 
|  | 1585 | { | 
| Vernon Mauery | 8166c8d | 2019-05-23 11:22:30 -0700 | [diff] [blame] | 1586 | serial = std::get<uint64_t>(value); | 
| Vernon Mauery | 52ce662 | 2019-05-22 09:19:46 -0700 | [diff] [blame] | 1587 | } | 
|  | 1588 | else if (key == "certificate") | 
|  | 1589 | { | 
| Vernon Mauery | 8166c8d | 2019-05-23 11:22:30 -0700 | [diff] [blame] | 1590 | cert = std::get<std::string>(value); | 
| Vernon Mauery | 52ce662 | 2019-05-22 09:19:46 -0700 | [diff] [blame] | 1591 | } | 
|  | 1592 | } | 
|  | 1593 | } | 
|  | 1594 | catch (sdbusplus::exception::SdBusError &e) | 
|  | 1595 | { | 
|  | 1596 | std::cerr << "SDBus Error: " << e.what(); | 
|  | 1597 | return IPMI_CC_UNSPECIFIED_ERROR; | 
|  | 1598 | } | 
|  | 1599 |  | 
|  | 1600 | cert_info->cert_len = cert.size(); | 
|  | 1601 | cert_info->serial = serial; | 
|  | 1602 | // truncate subject so it fits in the 255-byte array (if necessary) | 
|  | 1603 | if (subject.size() > sizeof(cert_info->subject)) | 
|  | 1604 | subject.resize(sizeof(cert_info->subject)); | 
|  | 1605 | cert_info->subject_len = subject.size(); | 
|  | 1606 | std::copy(subject.begin(), subject.end(), cert_info->subject); | 
|  | 1607 |  | 
|  | 1608 | // Status code. | 
|  | 1609 | ipmi_ret_t rc = IPMI_CC_OK; | 
|  | 1610 | // make sure to account for the *actual* size of the subject | 
|  | 1611 | *data_len = sizeof(*cert_info) - sizeof(cert_info->subject) + | 
|  | 1612 | cert_info->subject_len; | 
|  | 1613 |  | 
|  | 1614 | return rc; | 
|  | 1615 | } | 
|  | 1616 |  | 
|  | 1617 | struct fw_cert_data_req | 
|  | 1618 | { | 
|  | 1619 | uint8_t cert_id; | 
|  | 1620 | uint16_t offset; | 
|  | 1621 | uint16_t count; | 
|  | 1622 | } __attribute__((packed)); | 
|  | 1623 |  | 
|  | 1624 | static ipmi_ret_t ipmi_firmware_get_root_cert_data( | 
|  | 1625 | ipmi_netfn_t netfn, ipmi_cmd_t cmd, ipmi_request_t request, | 
|  | 1626 | ipmi_response_t response, ipmi_data_len_t data_len, ipmi_context_t context) | 
|  | 1627 | { | 
|  | 1628 | if (DEBUG) | 
|  | 1629 | std::cerr << "Get FW root cert data\n"; | 
|  | 1630 |  | 
|  | 1631 | // request: | 
|  | 1632 | // Byte 1 - certificate ID: request which certificate (ignored) | 
|  | 1633 | // Byte 2-3 - offset within cert to start at | 
|  | 1634 | // Byte 4-5 - number of bytes to return | 
|  | 1635 |  | 
|  | 1636 | // response: | 
|  | 1637 | // Byte 1-N  - certificate data | 
|  | 1638 |  | 
|  | 1639 | if (*data_len != sizeof(fw_cert_data_req)) | 
|  | 1640 | return IPMI_CC_REQ_DATA_LEN_INVALID; | 
|  | 1641 |  | 
|  | 1642 | auto cert_data_req = reinterpret_cast<struct fw_cert_data_req *>(request); | 
| Vernon Mauery | 15419dd | 2019-05-24 09:40:30 -0700 | [diff] [blame] | 1643 | std::shared_ptr<sdbusplus::asio::connection> bus = getSdBus(); | 
|  | 1644 | auto method = bus->new_method_call( | 
| Vernon Mauery | 52ce662 | 2019-05-22 09:19:46 -0700 | [diff] [blame] | 1645 | FW_UPDATE_SERVER_DBUS_NAME, FW_UPDATE_SERVER_INFO_PATH, | 
|  | 1646 | "org.freedesktop.DBus.Properties", "Get"); | 
|  | 1647 | method.append(FW_UPDATE_SECURITY_INTERFACE, "certificate"); | 
|  | 1648 | ipmi::DbusVariant cert; | 
|  | 1649 | try | 
|  | 1650 | { | 
| Vernon Mauery | 15419dd | 2019-05-24 09:40:30 -0700 | [diff] [blame] | 1651 | auto reply = bus->call(method); | 
| Vernon Mauery | 52ce662 | 2019-05-22 09:19:46 -0700 | [diff] [blame] | 1652 | reply.read(cert); | 
|  | 1653 | } | 
|  | 1654 | catch (sdbusplus::exception::SdBusError &e) | 
|  | 1655 | { | 
|  | 1656 | std::cerr << "SDBus Error: " << e.what(); | 
|  | 1657 | return IPMI_CC_UNSPECIFIED_ERROR; | 
|  | 1658 | } | 
| Vernon Mauery | 8166c8d | 2019-05-23 11:22:30 -0700 | [diff] [blame] | 1659 | auto cert_data = std::get<std::string>(cert); | 
| Vernon Mauery | 52ce662 | 2019-05-22 09:19:46 -0700 | [diff] [blame] | 1660 |  | 
|  | 1661 | if (cert_data_req->offset >= cert_data.size()) | 
|  | 1662 | { | 
|  | 1663 | *data_len = 0; | 
|  | 1664 | return IPMI_CC_INVALID_FIELD_REQUEST; | 
|  | 1665 | } | 
|  | 1666 | auto first = cert_data.begin() + cert_data_req->offset; | 
|  | 1667 | auto last = first + cert_data_req->count; | 
|  | 1668 | if (last > cert_data.end()) | 
|  | 1669 | last = cert_data.end(); | 
|  | 1670 |  | 
|  | 1671 | auto data_out = reinterpret_cast<char *>(response); | 
|  | 1672 | std::copy(first, last, data_out); | 
|  | 1673 |  | 
|  | 1674 | // Status code. | 
|  | 1675 | ipmi_ret_t rc = IPMI_CC_OK; | 
|  | 1676 | *data_len = (last - first); | 
|  | 1677 |  | 
|  | 1678 | return rc; | 
|  | 1679 | } | 
|  | 1680 |  | 
|  | 1681 | static ipmi_ret_t ipmi_firmware_write_data(ipmi_netfn_t netfn, ipmi_cmd_t cmd, | 
|  | 1682 | ipmi_request_t request, | 
|  | 1683 | ipmi_response_t response, | 
|  | 1684 | ipmi_data_len_t data_len, | 
|  | 1685 | ipmi_context_t context) | 
|  | 1686 | { | 
|  | 1687 | if (DEBUG) | 
|  | 1688 | std::cerr << "write fw data (" << *data_len << " bytes)\n"; | 
|  | 1689 |  | 
|  | 1690 | auto bytes_in = *data_len; | 
|  | 1691 | *data_len = 0; | 
|  | 1692 | if (fw_update_status.state() != fw_update_status_cache::FW_STATE_DOWNLOAD) | 
|  | 1693 | return IPMI_CC_INVALID; | 
|  | 1694 |  | 
|  | 1695 | std::ofstream out(FIRMWARE_BUFFER_FILE, | 
|  | 1696 | std::ofstream::binary | std::ofstream::app); | 
|  | 1697 | if (!out) | 
|  | 1698 | { | 
|  | 1699 | return IPMI_CC_UNSPECIFIED_ERROR; | 
|  | 1700 | } | 
|  | 1701 | if (out.tellp() > FIRMWARE_BUFFER_MAX_SIZE) | 
|  | 1702 | { | 
|  | 1703 | return IPMI_CC_INVALID_FIELD_REQUEST; | 
|  | 1704 | } | 
|  | 1705 | auto data = reinterpret_cast<uint8_t *>(request); | 
|  | 1706 | out.write(reinterpret_cast<char *>(data), bytes_in); | 
|  | 1707 | out.close(); | 
|  | 1708 | if (xfer_hash_check) | 
|  | 1709 | { | 
|  | 1710 | xfer_hash_check->hash({data, data + bytes_in}); | 
|  | 1711 | } | 
|  | 1712 |  | 
|  | 1713 | // Status code. | 
|  | 1714 | return IPMI_CC_OK; | 
|  | 1715 | } | 
|  | 1716 |  | 
|  | 1717 | static constexpr char NOT_IMPLEMENTED[] = "NOT IMPLEMENTED"; | 
|  | 1718 |  | 
|  | 1719 | static ipmi_ret_t ipmi_firmware_wildcard_handler( | 
|  | 1720 | ipmi_netfn_t netfn, ipmi_cmd_t cmd, ipmi_request_t request, | 
|  | 1721 | ipmi_response_t response, ipmi_data_len_t data_len, ipmi_context_t context) | 
|  | 1722 | { | 
|  | 1723 | if (DEBUG) | 
|  | 1724 | std::cerr << "Handling stubbed Netfn:[0x" << std::hex << +netfn | 
|  | 1725 | << "], Cmd:[0x" << std::hex << +cmd << "]\n"; | 
|  | 1726 |  | 
|  | 1727 | // Status code. | 
|  | 1728 | ipmi_ret_t rc = IPMI_CC_OK; | 
|  | 1729 |  | 
|  | 1730 | *data_len = sizeof(NOT_IMPLEMENTED); | 
|  | 1731 |  | 
|  | 1732 | // Now pack actual response | 
|  | 1733 | char *msg_reply = static_cast<char *>(response); | 
|  | 1734 | std::copy(std::begin(NOT_IMPLEMENTED), std::end(NOT_IMPLEMENTED), | 
|  | 1735 | msg_reply); | 
|  | 1736 |  | 
|  | 1737 | return rc; | 
|  | 1738 | } | 
|  | 1739 |  | 
|  | 1740 | struct intc_app_get_buffer_size_resp | 
|  | 1741 | { | 
|  | 1742 | uint8_t kcs_size; | 
|  | 1743 | uint8_t ipmb_size; | 
|  | 1744 | } __attribute__((packed)); | 
|  | 1745 |  | 
|  | 1746 | static constexpr int KCS_MAX_BUFFER_SIZE = 63; | 
|  | 1747 | static constexpr int IPMB_MAX_BUFFER_SIZE = 128; | 
|  | 1748 | static ipmi_ret_t ipmi_intel_app_get_buffer_size( | 
|  | 1749 | ipmi_netfn_t netfn, ipmi_cmd_t cmd, ipmi_request_t request, | 
|  | 1750 | ipmi_response_t response, ipmi_data_len_t data_len, ipmi_context_t context) | 
|  | 1751 | { | 
|  | 1752 | auto msg_reply = | 
|  | 1753 | reinterpret_cast<intc_app_get_buffer_size_resp *>(response); | 
|  | 1754 | // for now this is hard coded; really this number is dependent on | 
|  | 1755 | // the BMC kcs driver as well as the host kcs driver.... | 
|  | 1756 | // we can't know the latter. | 
|  | 1757 | msg_reply->kcs_size = KCS_MAX_BUFFER_SIZE / 4; | 
|  | 1758 | msg_reply->ipmb_size = IPMB_MAX_BUFFER_SIZE / 4; | 
|  | 1759 | *data_len = sizeof(*msg_reply); | 
|  | 1760 |  | 
|  | 1761 | return IPMI_CC_OK; | 
|  | 1762 | } | 
|  | 1763 |  | 
|  | 1764 | static constexpr ipmi_cmd_t IPMI_CMD_FW_GET_FW_VERSION_INFO = 0x20; | 
|  | 1765 | static constexpr ipmi_cmd_t IPMI_CMD_FW_GET_FW_SEC_VERSION_INFO = 0x21; | 
|  | 1766 | static constexpr ipmi_cmd_t IPMI_CMD_FW_GET_FW_UPD_CHAN_INFO = 0x22; | 
|  | 1767 | static constexpr ipmi_cmd_t IPMI_CMD_FW_GET_BMC_EXEC_CTX = 0x23; | 
|  | 1768 | static constexpr ipmi_cmd_t IPMI_CMD_FW_GET_ROOT_CERT_INFO = 0x24; | 
|  | 1769 | static constexpr ipmi_cmd_t IPMI_CMD_FW_GET_ROOT_CERT_DATA = 0x25; | 
|  | 1770 | static constexpr ipmi_cmd_t IPMI_CMD_FW_GET_FW_UPDATE_RAND_NUM = 0x26; | 
|  | 1771 | static constexpr ipmi_cmd_t IPMI_CMD_FW_SET_FW_UPDATE_MODE = 0x27; | 
|  | 1772 | static constexpr ipmi_cmd_t IPMI_CMD_FW_EXIT_FW_UPDATE_MODE = 0x28; | 
|  | 1773 | static constexpr ipmi_cmd_t IPMI_CMD_FW_UPDATE_CONTROL = 0x29; | 
|  | 1774 | static constexpr ipmi_cmd_t IPMI_CMD_FW_GET_STATUS = 0x2a; | 
|  | 1775 | static constexpr ipmi_cmd_t IPMI_CMD_FW_SET_FW_UPDATE_OPTIONS = 0x2b; | 
|  | 1776 | static constexpr ipmi_cmd_t IPMI_CMD_FW_IMAGE_WRITE = 0x2c; | 
|  | 1777 | static constexpr ipmi_cmd_t IPMI_CMD_FW_GET_TIMESTAMP = 0x2d; | 
|  | 1778 | static constexpr ipmi_cmd_t IPMI_CMD_FW_GET_UPDATE_ERR_MSG = 0xe0; | 
|  | 1779 | static constexpr ipmi_cmd_t IPMI_CMD_FW_GET_REMOTE_FW_INFO = 0xf0; | 
|  | 1780 |  | 
|  | 1781 | static constexpr ipmi_netfn_t NETFUN_INTC_APP = 0x30; | 
|  | 1782 | static constexpr ipmi_cmd_t IPMI_CMD_INTC_GET_BUFFER_SIZE = 0x66; | 
|  | 1783 |  | 
|  | 1784 | static void register_netfn_firmware_functions() | 
|  | 1785 | { | 
|  | 1786 | // guarantee that we start with an already timed out timestamp | 
|  | 1787 | fw_random_number_timestamp = | 
|  | 1788 | std::chrono::steady_clock::now() - FW_RANDOM_NUMBER_TTL; | 
|  | 1789 |  | 
|  | 1790 | unlink(FIRMWARE_BUFFER_FILE); | 
|  | 1791 |  | 
|  | 1792 | // <Get BT Interface Capabilities> | 
|  | 1793 | if (DEBUG) | 
|  | 1794 | std::cerr << "Registering firmware update commands\n"; | 
|  | 1795 |  | 
|  | 1796 | // get firmware version information | 
|  | 1797 | ipmi_register_callback(NETFUN_FIRMWARE, IPMI_CMD_FW_GET_FW_VERSION_INFO, | 
|  | 1798 | NULL, ipmi_firmware_get_fw_version_info, | 
|  | 1799 | PRIVILEGE_ADMIN); | 
|  | 1800 |  | 
|  | 1801 | // get firmware security version information | 
|  | 1802 | ipmi_register_callback(NETFUN_FIRMWARE, IPMI_CMD_FW_GET_FW_SEC_VERSION_INFO, | 
|  | 1803 | NULL, ipmi_firmware_get_fw_security_revision, | 
|  | 1804 | PRIVILEGE_ADMIN); | 
|  | 1805 |  | 
|  | 1806 | // get channel information (max transfer sizes) | 
|  | 1807 | ipmi_register_callback(NETFUN_FIRMWARE, IPMI_CMD_FW_GET_FW_UPD_CHAN_INFO, | 
|  | 1808 | NULL, ipmi_firmware_max_transfer_size, | 
|  | 1809 | PRIVILEGE_ADMIN); | 
|  | 1810 |  | 
|  | 1811 | // get bmc execution context | 
|  | 1812 | ipmi_register_callback(NETFUN_FIRMWARE, IPMI_CMD_FW_GET_BMC_EXEC_CTX, NULL, | 
|  | 1813 | ipmi_firmware_get_fw_execution_context, | 
|  | 1814 | PRIVILEGE_ADMIN); | 
|  | 1815 |  | 
|  | 1816 | // get root certificate information | 
|  | 1817 | ipmi_register_callback(NETFUN_FIRMWARE, IPMI_CMD_FW_GET_ROOT_CERT_INFO, | 
|  | 1818 | NULL, ipmi_firmware_get_root_cert_info, | 
|  | 1819 | PRIVILEGE_ADMIN); | 
|  | 1820 |  | 
|  | 1821 | // get root certificate data | 
|  | 1822 | ipmi_register_callback(NETFUN_FIRMWARE, IPMI_CMD_FW_GET_ROOT_CERT_DATA, | 
|  | 1823 | NULL, ipmi_firmware_get_root_cert_data, | 
|  | 1824 | PRIVILEGE_ADMIN); | 
|  | 1825 |  | 
|  | 1826 | // generate bmc fw update random number (for enter fw tranfer mode) | 
|  | 1827 | ipmi_register_callback(NETFUN_FIRMWARE, IPMI_CMD_FW_GET_FW_UPDATE_RAND_NUM, | 
|  | 1828 | NULL, ipmi_firmware_get_fw_random_number, | 
|  | 1829 | PRIVILEGE_ADMIN); | 
|  | 1830 |  | 
|  | 1831 | // enter firmware update mode | 
|  | 1832 | ipmi_register_callback(NETFUN_FIRMWARE, IPMI_CMD_FW_SET_FW_UPDATE_MODE, | 
|  | 1833 | NULL, ipmi_firmware_enter_fw_transfer_mode, | 
|  | 1834 | PRIVILEGE_ADMIN); | 
|  | 1835 |  | 
|  | 1836 | // exit firmware update mode | 
|  | 1837 | ipmi_register_callback(NETFUN_FIRMWARE, IPMI_CMD_FW_EXIT_FW_UPDATE_MODE, | 
|  | 1838 | NULL, ipmi_firmware_exit_fw_update_mode, | 
|  | 1839 | PRIVILEGE_ADMIN); | 
|  | 1840 |  | 
|  | 1841 | // firmware control mechanism (set filename, usb, etc.) | 
|  | 1842 | ipmi_register_callback(NETFUN_FIRMWARE, IPMI_CMD_FW_UPDATE_CONTROL, NULL, | 
|  | 1843 | ipmi_firmware_control, PRIVILEGE_ADMIN); | 
|  | 1844 |  | 
|  | 1845 | // get firmware update status | 
|  | 1846 | ipmi_register_callback(NETFUN_FIRMWARE, IPMI_CMD_FW_GET_STATUS, NULL, | 
|  | 1847 | ipmi_firmware_get_status, PRIVILEGE_ADMIN); | 
|  | 1848 |  | 
|  | 1849 | // set firmware update options (no downgrade, etc.) | 
|  | 1850 | ipmi_register_callback(NETFUN_FIRMWARE, IPMI_CMD_FW_SET_FW_UPDATE_OPTIONS, | 
|  | 1851 | NULL, ipmi_firmware_update_options, PRIVILEGE_ADMIN); | 
|  | 1852 |  | 
|  | 1853 | // write image data | 
|  | 1854 | ipmi_register_callback(NETFUN_FIRMWARE, IPMI_CMD_FW_IMAGE_WRITE, NULL, | 
|  | 1855 | ipmi_firmware_write_data, PRIVILEGE_ADMIN); | 
|  | 1856 |  | 
|  | 1857 | // get update timestamps | 
|  | 1858 | ipmi_register_callback(NETFUN_FIRMWARE, IPMI_CMD_FW_GET_TIMESTAMP, NULL, | 
|  | 1859 | ipmi_firmware_wildcard_handler, PRIVILEGE_ADMIN); | 
|  | 1860 |  | 
|  | 1861 | // get error message (when in error state) | 
|  | 1862 | ipmi_register_callback(NETFUN_FIRMWARE, IPMI_CMD_FW_GET_UPDATE_ERR_MSG, | 
|  | 1863 | NULL, ipmi_firmware_wildcard_handler, | 
|  | 1864 | PRIVILEGE_ADMIN); | 
|  | 1865 |  | 
|  | 1866 | // get remote firmware information (PSU, HSBP, etc.) | 
|  | 1867 | ipmi_register_callback(NETFUN_FIRMWARE, IPMI_CMD_FW_GET_REMOTE_FW_INFO, | 
|  | 1868 | NULL, ipmi_firmware_wildcard_handler, | 
|  | 1869 | PRIVILEGE_ADMIN); | 
|  | 1870 |  | 
|  | 1871 | // <Wildcard Command> | 
|  | 1872 | ipmi_register_callback(NETFUN_FIRMWARE, IPMI_CMD_WILDCARD, NULL, | 
|  | 1873 | ipmi_firmware_wildcard_handler, PRIVILEGE_ADMIN); | 
|  | 1874 |  | 
|  | 1875 | // get buffer size is used by fw update (exclusively?) | 
|  | 1876 | ipmi_register_callback(NETFUN_INTC_APP, IPMI_CMD_INTC_GET_BUFFER_SIZE, NULL, | 
|  | 1877 | ipmi_intel_app_get_buffer_size, PRIVILEGE_USER); | 
|  | 1878 | return; | 
|  | 1879 | } |