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