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