1 #include "bios.hpp" 2 3 #include "common/utils.hpp" 4 5 #include <phosphor-logging/lg2.hpp> 6 7 #include <array> 8 #include <chrono> 9 #include <ctime> 10 #include <stdexcept> 11 #include <string> 12 #include <variant> 13 #include <vector> 14 15 PHOSPHOR_LOG2_USING; 16 17 using namespace pldm::utils; 18 19 namespace pldm 20 { 21 namespace responder 22 { 23 namespace utils 24 { 25 void epochToBCDTime(uint64_t timeSec, uint8_t& seconds, uint8_t& minutes, 26 uint8_t& hours, uint8_t& day, uint8_t& month, 27 uint16_t& year) 28 { 29 auto t = time_t(timeSec); 30 auto time = localtime(&t); 31 32 seconds = pldm::utils::decimalToBcd(time->tm_sec); 33 minutes = pldm::utils::decimalToBcd(time->tm_min); 34 hours = pldm::utils::decimalToBcd(time->tm_hour); 35 day = pldm::utils::decimalToBcd(time->tm_mday); 36 month = pldm::utils::decimalToBcd( 37 time->tm_mon + 1); // The number of months in the range 38 // 0 to 11.PLDM expects range 1 to 12 39 year = pldm::utils::decimalToBcd( 40 time->tm_year + 1900); // The number of years since 1900 41 } 42 43 std::time_t timeToEpoch(uint8_t seconds, uint8_t minutes, uint8_t hours, 44 uint8_t day, uint8_t month, uint16_t year) 45 { 46 struct std::tm stm; 47 48 stm.tm_year = year - 1900; 49 stm.tm_mon = month - 1; 50 stm.tm_mday = day; 51 stm.tm_hour = hours; 52 stm.tm_min = minutes; 53 stm.tm_sec = seconds; 54 stm.tm_isdst = -1; 55 56 // It will get the time in seconds since 57 // Epoch, 1970.1.1 00:00:00 +0000,UTC. 58 return timegm(&stm); 59 } 60 61 } // namespace utils 62 63 namespace bios 64 { 65 using EpochTimeUS = uint64_t; 66 67 DBusHandler dbusHandler; 68 69 Handler::Handler( 70 int fd, uint8_t eid, pldm::InstanceIdDb* instanceIdDb, 71 pldm::requester::Handler<pldm::requester::Request>* handler, 72 pldm::responder::platform_config::Handler* platformConfigHandler, 73 pldm::responder::bios::Callback requestPLDMServiceName) : 74 biosConfig(BIOS_JSONS_DIR, BIOS_TABLES_DIR, &dbusHandler, fd, eid, 75 instanceIdDb, handler, platformConfigHandler, 76 requestPLDMServiceName) 77 { 78 handlers.emplace( 79 PLDM_SET_DATE_TIME, 80 [this](pldm_tid_t, const pldm_msg* request, size_t payloadLength) { 81 return this->setDateTime(request, payloadLength); 82 }); 83 handlers.emplace( 84 PLDM_GET_DATE_TIME, 85 [this](pldm_tid_t, const pldm_msg* request, size_t payloadLength) { 86 return this->getDateTime(request, payloadLength); 87 }); 88 handlers.emplace( 89 PLDM_GET_BIOS_TABLE, 90 [this](pldm_tid_t, const pldm_msg* request, size_t payloadLength) { 91 return this->getBIOSTable(request, payloadLength); 92 }); 93 handlers.emplace( 94 PLDM_SET_BIOS_TABLE, 95 [this](pldm_tid_t, const pldm_msg* request, size_t payloadLength) { 96 return this->setBIOSTable(request, payloadLength); 97 }); 98 handlers.emplace( 99 PLDM_GET_BIOS_ATTRIBUTE_CURRENT_VALUE_BY_HANDLE, 100 [this](pldm_tid_t, const pldm_msg* request, size_t payloadLength) { 101 return this->getBIOSAttributeCurrentValueByHandle(request, 102 payloadLength); 103 }); 104 handlers.emplace( 105 PLDM_SET_BIOS_ATTRIBUTE_CURRENT_VALUE, 106 [this](pldm_tid_t, const pldm_msg* request, size_t payloadLength) { 107 return this->setBIOSAttributeCurrentValue(request, payloadLength); 108 }); 109 } 110 111 Response Handler::getDateTime(const pldm_msg* request, size_t /*payloadLength*/) 112 { 113 uint8_t seconds = 0; 114 uint8_t minutes = 0; 115 uint8_t hours = 0; 116 uint8_t day = 0; 117 uint8_t month = 0; 118 uint16_t year = 0; 119 120 constexpr auto timeInterface = "xyz.openbmc_project.Time.EpochTime"; 121 constexpr auto bmcTimePath = "/xyz/openbmc_project/time/bmc"; 122 Response response(sizeof(pldm_msg_hdr) + PLDM_GET_DATE_TIME_RESP_BYTES, 0); 123 auto responsePtr = reinterpret_cast<pldm_msg*>(response.data()); 124 EpochTimeUS timeUsec; 125 126 try 127 { 128 timeUsec = pldm::utils::DBusHandler().getDbusProperty<EpochTimeUS>( 129 bmcTimePath, "Elapsed", timeInterface); 130 } 131 catch (const sdbusplus::exception_t& e) 132 { 133 error( 134 "Error getting time from Elapsed property at path '{PATH}' on interface '{INTERFACE}': {ERROR}", 135 "PATH", bmcTimePath, "INTERFACE", timeInterface, "ERROR", e); 136 return CmdHandler::ccOnlyResponse(request, PLDM_ERROR); 137 } 138 139 uint64_t timeSec = std::chrono::duration_cast<std::chrono::seconds>( 140 std::chrono::microseconds(timeUsec)) 141 .count(); 142 143 pldm::responder::utils::epochToBCDTime(timeSec, seconds, minutes, hours, 144 day, month, year); 145 146 auto rc = encode_get_date_time_resp(request->hdr.instance_id, PLDM_SUCCESS, 147 seconds, minutes, hours, day, month, 148 year, responsePtr); 149 if (rc != PLDM_SUCCESS) 150 { 151 return ccOnlyResponse(request, rc); 152 } 153 154 return response; 155 } 156 157 Response Handler::setDateTime(const pldm_msg* request, size_t payloadLength) 158 { 159 uint8_t seconds = 0; 160 uint8_t minutes = 0; 161 uint8_t hours = 0; 162 uint8_t day = 0; 163 uint8_t month = 0; 164 uint16_t year = 0; 165 std::time_t timeSec; 166 167 constexpr auto timeSyncPath = "/xyz/openbmc_project/time/sync_method"; 168 constexpr auto timeSyncInterface = 169 "xyz.openbmc_project.Time.Synchronization"; 170 constexpr auto timeSyncProperty = "TimeSyncMethod"; 171 172 // The time is correct on BMC when in NTP mode, so we do not want to 173 // try and set the time again and cause potential time drifts. 174 try 175 { 176 auto propVal = pldm::utils::DBusHandler().getDbusPropertyVariant( 177 timeSyncPath, timeSyncProperty, timeSyncInterface); 178 const auto& mode = std::get<std::string>(propVal); 179 180 if (mode == "xyz.openbmc_project.Time.Synchronization.Method.NTP") 181 { 182 return ccOnlyResponse(request, PLDM_SUCCESS); 183 } 184 } 185 catch (const std::exception& e) 186 { 187 error( 188 "Failed to get the time sync property from path {TIME_SYNC_PATH}, interface '{SYNC_INTERFACE}' and property '{SYNC_PROPERTY}', error - '{ERROR}'", 189 "TIME_SYNC_PATH", timeSyncPath, "SYNC_INTERFACE", timeSyncInterface, 190 "SYNC_PROPERTY", timeSyncProperty, "ERROR", e); 191 } 192 193 constexpr auto setTimeInterface = "xyz.openbmc_project.Time.EpochTime"; 194 constexpr auto setTimePath = "/xyz/openbmc_project/time/bmc"; 195 constexpr auto timeSetPro = "Elapsed"; 196 197 auto rc = decode_set_date_time_req(request, payloadLength, &seconds, 198 &minutes, &hours, &day, &month, &year); 199 if (rc != PLDM_SUCCESS) 200 { 201 return ccOnlyResponse(request, rc); 202 } 203 timeSec = pldm::responder::utils::timeToEpoch(seconds, minutes, hours, day, 204 month, year); 205 uint64_t timeUsec = std::chrono::duration_cast<std::chrono::microseconds>( 206 std::chrono::seconds(timeSec)) 207 .count(); 208 PropertyValue value{timeUsec}; 209 try 210 { 211 DBusMapping dbusMapping{setTimePath, setTimeInterface, timeSetPro, 212 "uint64_t"}; 213 pldm::utils::DBusHandler().setDbusProperty(dbusMapping, value); 214 } 215 catch (const std::exception& e) 216 { 217 error( 218 "Failed to set time at {SET_TIME_PATH}, interface '{TIME_INTERFACE}' and error - {ERROR}", 219 "SET_TIME_PATH", setTimePath, "TIME_INTERFACE", setTimeInterface, 220 "ERROR", e); 221 return ccOnlyResponse(request, PLDM_ERROR); 222 } 223 224 return ccOnlyResponse(request, PLDM_SUCCESS); 225 } 226 227 Response Handler::getBIOSTable(const pldm_msg* request, size_t payloadLength) 228 { 229 uint32_t transferHandle{}; 230 uint8_t transferOpFlag{}; 231 uint8_t tableType{}; 232 233 auto rc = decode_get_bios_table_req(request, payloadLength, &transferHandle, 234 &transferOpFlag, &tableType); 235 if (rc != PLDM_SUCCESS) 236 { 237 return ccOnlyResponse(request, rc); 238 } 239 240 auto table = 241 biosConfig.getBIOSTable(static_cast<pldm_bios_table_types>(tableType)); 242 if (!table) 243 { 244 return ccOnlyResponse(request, PLDM_BIOS_TABLE_UNAVAILABLE); 245 } 246 247 Response response(sizeof(pldm_msg_hdr) + 248 PLDM_GET_BIOS_TABLE_MIN_RESP_BYTES + table->size()); 249 auto responsePtr = reinterpret_cast<pldm_msg*>(response.data()); 250 251 rc = encode_get_bios_table_resp( 252 request->hdr.instance_id, PLDM_SUCCESS, 0 /* nxtTransferHandle */, 253 PLDM_START_AND_END, table->data(), response.size(), responsePtr); 254 if (rc != PLDM_SUCCESS) 255 { 256 return ccOnlyResponse(request, rc); 257 } 258 259 return response; 260 } 261 262 Response Handler::setBIOSTable(const pldm_msg* request, size_t payloadLength) 263 { 264 uint32_t transferHandle{}; 265 uint8_t transferOpFlag{}; 266 uint8_t tableType{}; 267 struct variable_field field; 268 269 auto rc = decode_set_bios_table_req(request, payloadLength, &transferHandle, 270 &transferOpFlag, &tableType, &field); 271 if (rc != PLDM_SUCCESS) 272 { 273 return ccOnlyResponse(request, rc); 274 } 275 276 Table table(field.ptr, field.ptr + field.length); 277 rc = biosConfig.setBIOSTable(tableType, table); 278 if (rc != PLDM_SUCCESS) 279 { 280 return ccOnlyResponse(request, rc); 281 } 282 283 Response response(sizeof(pldm_msg_hdr) + PLDM_SET_BIOS_TABLE_RESP_BYTES); 284 auto responsePtr = reinterpret_cast<pldm_msg*>(response.data()); 285 286 rc = encode_set_bios_table_resp(request->hdr.instance_id, PLDM_SUCCESS, 287 0 /* nxtTransferHandle */, responsePtr); 288 if (rc != PLDM_SUCCESS) 289 { 290 return ccOnlyResponse(request, rc); 291 } 292 293 return response; 294 } 295 296 Response Handler::getBIOSAttributeCurrentValueByHandle(const pldm_msg* request, 297 size_t payloadLength) 298 { 299 uint32_t transferHandle; 300 uint8_t transferOpFlag; 301 uint16_t attributeHandle; 302 303 auto rc = decode_get_bios_attribute_current_value_by_handle_req( 304 request, payloadLength, &transferHandle, &transferOpFlag, 305 &attributeHandle); 306 if (rc != PLDM_SUCCESS) 307 { 308 return ccOnlyResponse(request, rc); 309 } 310 311 auto table = biosConfig.getBIOSTable(PLDM_BIOS_ATTR_VAL_TABLE); 312 if (!table) 313 { 314 return ccOnlyResponse(request, PLDM_BIOS_TABLE_UNAVAILABLE); 315 } 316 317 auto entry = pldm_bios_table_attr_value_find_by_handle( 318 table->data(), table->size(), attributeHandle); 319 if (entry == nullptr) 320 { 321 return ccOnlyResponse(request, PLDM_INVALID_BIOS_ATTR_HANDLE); 322 } 323 324 auto entryLength = pldm_bios_table_attr_value_entry_length(entry); 325 Response response(sizeof(pldm_msg_hdr) + 326 PLDM_GET_BIOS_ATTR_CURR_VAL_BY_HANDLE_MIN_RESP_BYTES + 327 entryLength, 328 0); 329 auto responsePtr = reinterpret_cast<pldm_msg*>(response.data()); 330 rc = encode_get_bios_current_value_by_handle_resp( 331 request->hdr.instance_id, PLDM_SUCCESS, 0, PLDM_START_AND_END, 332 reinterpret_cast<const uint8_t*>(entry), entryLength, responsePtr); 333 if (rc != PLDM_SUCCESS) 334 { 335 return ccOnlyResponse(request, rc); 336 } 337 338 return response; 339 } 340 341 Response Handler::setBIOSAttributeCurrentValue(const pldm_msg* request, 342 size_t payloadLength) 343 { 344 uint32_t transferHandle; 345 uint8_t transferOpFlag; 346 variable_field attributeField; 347 348 auto rc = decode_set_bios_attribute_current_value_req( 349 request, payloadLength, &transferHandle, &transferOpFlag, 350 &attributeField); 351 if (rc != PLDM_SUCCESS) 352 { 353 return ccOnlyResponse(request, rc); 354 } 355 356 rc = biosConfig.setAttrValue(attributeField.ptr, attributeField.length, 357 false); 358 359 Response response( 360 sizeof(pldm_msg_hdr) + PLDM_SET_BIOS_ATTR_CURR_VAL_RESP_BYTES, 0); 361 auto responsePtr = reinterpret_cast<pldm_msg*>(response.data()); 362 363 encode_set_bios_attribute_current_value_resp(request->hdr.instance_id, rc, 364 0, responsePtr); 365 366 return response; 367 } 368 369 } // namespace bios 370 } // namespace responder 371 } // namespace pldm 372