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 biosConfig(BIOS_JSONS_DIR, BIOS_TABLES_DIR, &dbusHandler, fd, eid, 76 instanceIdDb, handler, platformConfigHandler) 77 { 78 biosConfig.removeTables(); 79 biosConfig.buildTables(); 80 81 handlers.emplace( 82 PLDM_SET_DATE_TIME, 83 [this](pldm_tid_t, const pldm_msg* request, size_t payloadLength) { 84 return this->setDateTime(request, payloadLength); 85 }); 86 handlers.emplace( 87 PLDM_GET_DATE_TIME, 88 [this](pldm_tid_t, const pldm_msg* request, size_t payloadLength) { 89 return this->getDateTime(request, payloadLength); 90 }); 91 handlers.emplace( 92 PLDM_GET_BIOS_TABLE, 93 [this](pldm_tid_t, const pldm_msg* request, size_t payloadLength) { 94 return this->getBIOSTable(request, payloadLength); 95 }); 96 handlers.emplace( 97 PLDM_SET_BIOS_TABLE, 98 [this](pldm_tid_t, const pldm_msg* request, size_t payloadLength) { 99 return this->setBIOSTable(request, payloadLength); 100 }); 101 handlers.emplace( 102 PLDM_GET_BIOS_ATTRIBUTE_CURRENT_VALUE_BY_HANDLE, 103 [this](pldm_tid_t, const pldm_msg* request, size_t payloadLength) { 104 return this->getBIOSAttributeCurrentValueByHandle(request, 105 payloadLength); 106 }); 107 handlers.emplace( 108 PLDM_SET_BIOS_ATTRIBUTE_CURRENT_VALUE, 109 [this](pldm_tid_t, const pldm_msg* request, size_t payloadLength) { 110 return this->setBIOSAttributeCurrentValue(request, payloadLength); 111 }); 112 } 113 114 Response Handler::getDateTime(const pldm_msg* request, size_t /*payloadLength*/) 115 { 116 uint8_t seconds = 0; 117 uint8_t minutes = 0; 118 uint8_t hours = 0; 119 uint8_t day = 0; 120 uint8_t month = 0; 121 uint16_t year = 0; 122 123 constexpr auto timeInterface = "xyz.openbmc_project.Time.EpochTime"; 124 constexpr auto bmcTimePath = "/xyz/openbmc_project/time/bmc"; 125 Response response(sizeof(pldm_msg_hdr) + PLDM_GET_DATE_TIME_RESP_BYTES, 0); 126 auto responsePtr = reinterpret_cast<pldm_msg*>(response.data()); 127 EpochTimeUS timeUsec; 128 129 try 130 { 131 timeUsec = pldm::utils::DBusHandler().getDbusProperty<EpochTimeUS>( 132 bmcTimePath, "Elapsed", timeInterface); 133 } 134 catch (const sdbusplus::exception_t& e) 135 { 136 error( 137 "Error getting time from Elapsed property at '{PATH}' on '{INTERFACE}': {ERROR}", 138 "PATH", bmcTimePath, "INTERFACE", timeInterface, "ERROR", e); 139 return CmdHandler::ccOnlyResponse(request, PLDM_ERROR); 140 } 141 142 uint64_t timeSec = std::chrono::duration_cast<std::chrono::seconds>( 143 std::chrono::microseconds(timeUsec)) 144 .count(); 145 146 pldm::responder::utils::epochToBCDTime(timeSec, seconds, minutes, hours, 147 day, month, year); 148 149 auto rc = encode_get_date_time_resp(request->hdr.instance_id, PLDM_SUCCESS, 150 seconds, minutes, hours, day, month, 151 year, responsePtr); 152 if (rc != PLDM_SUCCESS) 153 { 154 return ccOnlyResponse(request, rc); 155 } 156 157 return response; 158 } 159 160 Response Handler::setDateTime(const pldm_msg* request, size_t payloadLength) 161 { 162 uint8_t seconds = 0; 163 uint8_t minutes = 0; 164 uint8_t hours = 0; 165 uint8_t day = 0; 166 uint8_t month = 0; 167 uint16_t year = 0; 168 std::time_t timeSec; 169 170 constexpr auto timeSyncPath = "/xyz/openbmc_project/time/sync_method"; 171 constexpr auto timeSyncInterface = 172 "xyz.openbmc_project.Time.Synchronization"; 173 constexpr auto timeSyncProperty = "TimeSyncMethod"; 174 175 // The time is correct on BMC when in NTP mode, so we do not want to 176 // try and set the time again and cause potential time drifts. 177 try 178 { 179 auto propVal = pldm::utils::DBusHandler().getDbusPropertyVariant( 180 timeSyncPath, timeSyncProperty, timeSyncInterface); 181 const auto& mode = std::get<std::string>(propVal); 182 183 if (mode == "xyz.openbmc_project.Time.Synchronization.Method.NTP") 184 { 185 return ccOnlyResponse(request, PLDM_SUCCESS); 186 } 187 } 188 catch (const std::exception& e) 189 { 190 error( 191 "Error getting the time sync property, PATH={TIME_SYNC_PATH} INTERFACE={SYNC_INTERFACE} PROPERTY={SYNC_PROP} ERROR={ERR_EXCEP}", 192 "TIME_SYNC_PATH", timeSyncPath, "SYNC_INTERFACE", timeSyncInterface, 193 "SYNC_PROP", timeSyncProperty, "ERR_EXCEP", e.what()); 194 } 195 196 constexpr auto setTimeInterface = "xyz.openbmc_project.Time.EpochTime"; 197 constexpr auto setTimePath = "/xyz/openbmc_project/time/bmc"; 198 constexpr auto timeSetPro = "Elapsed"; 199 200 auto rc = decode_set_date_time_req(request, payloadLength, &seconds, 201 &minutes, &hours, &day, &month, &year); 202 if (rc != PLDM_SUCCESS) 203 { 204 return ccOnlyResponse(request, rc); 205 } 206 timeSec = pldm::responder::utils::timeToEpoch(seconds, minutes, hours, day, 207 month, year); 208 uint64_t timeUsec = std::chrono::duration_cast<std::chrono::microseconds>( 209 std::chrono::seconds(timeSec)) 210 .count(); 211 PropertyValue value{timeUsec}; 212 try 213 { 214 DBusMapping dbusMapping{setTimePath, setTimeInterface, timeSetPro, 215 "uint64_t"}; 216 pldm::utils::DBusHandler().setDbusProperty(dbusMapping, value); 217 } 218 catch (const std::exception& e) 219 { 220 error( 221 "Error Setting time,PATH={SET_TIME_PATH} TIME INTERFACE={TIME_INTERFACE} ERROR={ERR_EXCEP}", 222 "SET_TIME_PATH", setTimePath, "TIME_INTERFACE", setTimeInterface, 223 "ERR_EXCEP", e.what()); 224 return ccOnlyResponse(request, PLDM_ERROR); 225 } 226 227 return ccOnlyResponse(request, PLDM_SUCCESS); 228 } 229 230 Response Handler::getBIOSTable(const pldm_msg* request, size_t payloadLength) 231 { 232 uint32_t transferHandle{}; 233 uint8_t transferOpFlag{}; 234 uint8_t tableType{}; 235 236 auto rc = decode_get_bios_table_req(request, payloadLength, &transferHandle, 237 &transferOpFlag, &tableType); 238 if (rc != PLDM_SUCCESS) 239 { 240 return ccOnlyResponse(request, rc); 241 } 242 243 auto table = 244 biosConfig.getBIOSTable(static_cast<pldm_bios_table_types>(tableType)); 245 if (!table) 246 { 247 return ccOnlyResponse(request, PLDM_BIOS_TABLE_UNAVAILABLE); 248 } 249 250 Response response(sizeof(pldm_msg_hdr) + 251 PLDM_GET_BIOS_TABLE_MIN_RESP_BYTES + table->size()); 252 auto responsePtr = reinterpret_cast<pldm_msg*>(response.data()); 253 254 rc = encode_get_bios_table_resp( 255 request->hdr.instance_id, PLDM_SUCCESS, 0 /* nxtTransferHandle */, 256 PLDM_START_AND_END, table->data(), response.size(), responsePtr); 257 if (rc != PLDM_SUCCESS) 258 { 259 return ccOnlyResponse(request, rc); 260 } 261 262 return response; 263 } 264 265 Response Handler::setBIOSTable(const pldm_msg* request, size_t payloadLength) 266 { 267 uint32_t transferHandle{}; 268 uint8_t transferOpFlag{}; 269 uint8_t tableType{}; 270 struct variable_field field; 271 272 auto rc = decode_set_bios_table_req(request, payloadLength, &transferHandle, 273 &transferOpFlag, &tableType, &field); 274 if (rc != PLDM_SUCCESS) 275 { 276 return ccOnlyResponse(request, rc); 277 } 278 279 Table table(field.ptr, field.ptr + field.length); 280 rc = biosConfig.setBIOSTable(tableType, table); 281 if (rc != PLDM_SUCCESS) 282 { 283 return ccOnlyResponse(request, rc); 284 } 285 286 Response response(sizeof(pldm_msg_hdr) + PLDM_SET_BIOS_TABLE_RESP_BYTES); 287 auto responsePtr = reinterpret_cast<pldm_msg*>(response.data()); 288 289 rc = encode_set_bios_table_resp(request->hdr.instance_id, PLDM_SUCCESS, 290 0 /* nxtTransferHandle */, responsePtr); 291 if (rc != PLDM_SUCCESS) 292 { 293 return ccOnlyResponse(request, rc); 294 } 295 296 return response; 297 } 298 299 Response Handler::getBIOSAttributeCurrentValueByHandle(const pldm_msg* request, 300 size_t payloadLength) 301 { 302 uint32_t transferHandle; 303 uint8_t transferOpFlag; 304 uint16_t attributeHandle; 305 306 auto rc = decode_get_bios_attribute_current_value_by_handle_req( 307 request, payloadLength, &transferHandle, &transferOpFlag, 308 &attributeHandle); 309 if (rc != PLDM_SUCCESS) 310 { 311 return ccOnlyResponse(request, rc); 312 } 313 314 auto table = biosConfig.getBIOSTable(PLDM_BIOS_ATTR_VAL_TABLE); 315 if (!table) 316 { 317 return ccOnlyResponse(request, PLDM_BIOS_TABLE_UNAVAILABLE); 318 } 319 320 auto entry = pldm_bios_table_attr_value_find_by_handle( 321 table->data(), table->size(), attributeHandle); 322 if (entry == nullptr) 323 { 324 return ccOnlyResponse(request, PLDM_INVALID_BIOS_ATTR_HANDLE); 325 } 326 327 auto entryLength = pldm_bios_table_attr_value_entry_length(entry); 328 Response response(sizeof(pldm_msg_hdr) + 329 PLDM_GET_BIOS_ATTR_CURR_VAL_BY_HANDLE_MIN_RESP_BYTES + 330 entryLength, 331 0); 332 auto responsePtr = reinterpret_cast<pldm_msg*>(response.data()); 333 rc = encode_get_bios_current_value_by_handle_resp( 334 request->hdr.instance_id, PLDM_SUCCESS, 0, PLDM_START_AND_END, 335 reinterpret_cast<const uint8_t*>(entry), entryLength, responsePtr); 336 if (rc != PLDM_SUCCESS) 337 { 338 return ccOnlyResponse(request, rc); 339 } 340 341 return response; 342 } 343 344 Response Handler::setBIOSAttributeCurrentValue(const pldm_msg* request, 345 size_t payloadLength) 346 { 347 uint32_t transferHandle; 348 uint8_t transferOpFlag; 349 variable_field attributeField; 350 351 auto rc = decode_set_bios_attribute_current_value_req( 352 request, payloadLength, &transferHandle, &transferOpFlag, 353 &attributeField); 354 if (rc != PLDM_SUCCESS) 355 { 356 return ccOnlyResponse(request, rc); 357 } 358 359 rc = biosConfig.setAttrValue(attributeField.ptr, attributeField.length, 360 false); 361 362 Response response( 363 sizeof(pldm_msg_hdr) + PLDM_SET_BIOS_ATTR_CURR_VAL_RESP_BYTES, 0); 364 auto responsePtr = reinterpret_cast<pldm_msg*>(response.data()); 365 366 encode_set_bios_attribute_current_value_resp(request->hdr.instance_id, rc, 367 0, responsePtr); 368 369 return response; 370 } 371 372 } // namespace bios 373 } // namespace responder 374 } // namespace pldm 375