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