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