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