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