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 21 namespace responder 22 { 23 24 namespace utils 25 { 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 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( 99 request, payloadLength); 100 }); 101 handlers.emplace(PLDM_SET_BIOS_ATTRIBUTE_CURRENT_VALUE, 102 [this](const pldm_msg* request, size_t payloadLength) { 103 return this->setBIOSAttributeCurrentValue( 104 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::exception& e) 129 { 130 std::cerr << "Error getting time, PATH=" << bmcTimePath 131 << " TIME INTERACE=" << timeInterface << "\n"; 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 setTimeInterface = "xyz.openbmc_project.Time.EpochTime"; 165 constexpr auto setTimePath = "/xyz/openbmc_project/time/bmc"; 166 constexpr auto timeSetPro = "Elapsed"; 167 168 auto rc = decode_set_date_time_req(request, payloadLength, &seconds, 169 &minutes, &hours, &day, &month, &year); 170 if (rc != PLDM_SUCCESS) 171 { 172 return ccOnlyResponse(request, rc); 173 } 174 timeSec = pldm::responder::utils::timeToEpoch(seconds, minutes, hours, day, 175 month, year); 176 uint64_t timeUsec = std::chrono::duration_cast<std::chrono::microseconds>( 177 std::chrono::seconds(timeSec)) 178 .count(); 179 PropertyValue value{timeUsec}; 180 try 181 { 182 DBusMapping dbusMapping{setTimePath, setTimeInterface, timeSetPro, 183 "uint64_t"}; 184 pldm::utils::DBusHandler().setDbusProperty(dbusMapping, value); 185 } 186 catch (std::exception& e) 187 { 188 189 std::cerr << "Error Setting time,PATH=" << setTimePath 190 << "TIME INTERFACE=" << setTimeInterface 191 << "ERROR=" << e.what() << "\n"; 192 193 return ccOnlyResponse(request, PLDM_ERROR); 194 } 195 196 return ccOnlyResponse(request, PLDM_SUCCESS); 197 } 198 199 Response Handler::getBIOSTable(const pldm_msg* request, size_t payloadLength) 200 { 201 uint32_t transferHandle{}; 202 uint8_t transferOpFlag{}; 203 uint8_t tableType{}; 204 205 auto rc = decode_get_bios_table_req(request, payloadLength, &transferHandle, 206 &transferOpFlag, &tableType); 207 if (rc != PLDM_SUCCESS) 208 { 209 return ccOnlyResponse(request, rc); 210 } 211 212 auto table = 213 biosConfig.getBIOSTable(static_cast<pldm_bios_table_types>(tableType)); 214 if (!table) 215 { 216 return ccOnlyResponse(request, PLDM_BIOS_TABLE_UNAVAILABLE); 217 } 218 219 Response response(sizeof(pldm_msg_hdr) + 220 PLDM_GET_BIOS_TABLE_MIN_RESP_BYTES + table->size()); 221 auto responsePtr = reinterpret_cast<pldm_msg*>(response.data()); 222 223 rc = encode_get_bios_table_resp( 224 request->hdr.instance_id, PLDM_SUCCESS, 0 /* nxtTransferHandle */, 225 PLDM_START_AND_END, table->data(), response.size(), responsePtr); 226 if (rc != PLDM_SUCCESS) 227 { 228 return ccOnlyResponse(request, rc); 229 } 230 231 return response; 232 } 233 234 Response Handler::setBIOSTable(const pldm_msg* request, size_t payloadLength) 235 { 236 uint32_t transferHandle{}; 237 uint8_t transferOpFlag{}; 238 uint8_t tableType{}; 239 struct variable_field field; 240 241 auto rc = decode_set_bios_table_req(request, payloadLength, &transferHandle, 242 &transferOpFlag, &tableType, &field); 243 if (rc != PLDM_SUCCESS) 244 { 245 return ccOnlyResponse(request, rc); 246 } 247 248 Table table(field.ptr, field.ptr + field.length); 249 rc = biosConfig.setBIOSTable(tableType, table); 250 if (rc != PLDM_SUCCESS) 251 { 252 return ccOnlyResponse(request, rc); 253 } 254 255 Response response(sizeof(pldm_msg_hdr) + PLDM_SET_BIOS_TABLE_RESP_BYTES); 256 auto responsePtr = reinterpret_cast<pldm_msg*>(response.data()); 257 258 rc = encode_set_bios_table_resp(request->hdr.instance_id, PLDM_SUCCESS, 259 0 /* nxtTransferHandle */, responsePtr); 260 if (rc != PLDM_SUCCESS) 261 { 262 return ccOnlyResponse(request, rc); 263 } 264 265 return response; 266 } 267 268 Response Handler::getBIOSAttributeCurrentValueByHandle(const pldm_msg* request, 269 size_t payloadLength) 270 { 271 uint32_t transferHandle; 272 uint8_t transferOpFlag; 273 uint16_t attributeHandle; 274 275 auto rc = decode_get_bios_attribute_current_value_by_handle_req( 276 request, payloadLength, &transferHandle, &transferOpFlag, 277 &attributeHandle); 278 if (rc != PLDM_SUCCESS) 279 { 280 return ccOnlyResponse(request, rc); 281 } 282 283 auto table = biosConfig.getBIOSTable(PLDM_BIOS_ATTR_VAL_TABLE); 284 if (!table) 285 { 286 return ccOnlyResponse(request, PLDM_BIOS_TABLE_UNAVAILABLE); 287 } 288 289 auto entry = pldm_bios_table_attr_value_find_by_handle( 290 table->data(), table->size(), attributeHandle); 291 if (entry == nullptr) 292 { 293 return ccOnlyResponse(request, PLDM_INVALID_BIOS_ATTR_HANDLE); 294 } 295 296 auto entryLength = pldm_bios_table_attr_value_entry_length(entry); 297 Response response(sizeof(pldm_msg_hdr) + 298 PLDM_GET_BIOS_ATTR_CURR_VAL_BY_HANDLE_MIN_RESP_BYTES + 299 entryLength, 300 0); 301 auto responsePtr = reinterpret_cast<pldm_msg*>(response.data()); 302 rc = encode_get_bios_current_value_by_handle_resp( 303 request->hdr.instance_id, PLDM_SUCCESS, 0, PLDM_START_AND_END, 304 reinterpret_cast<const uint8_t*>(entry), entryLength, responsePtr); 305 if (rc != PLDM_SUCCESS) 306 { 307 return ccOnlyResponse(request, rc); 308 } 309 310 return response; 311 } 312 313 Response Handler::setBIOSAttributeCurrentValue(const pldm_msg* request, 314 size_t payloadLength) 315 { 316 uint32_t transferHandle; 317 uint8_t transferOpFlag; 318 variable_field attributeField; 319 320 auto rc = decode_set_bios_attribute_current_value_req( 321 request, payloadLength, &transferHandle, &transferOpFlag, 322 &attributeField); 323 if (rc != PLDM_SUCCESS) 324 { 325 return ccOnlyResponse(request, rc); 326 } 327 328 rc = biosConfig.setAttrValue(attributeField.ptr, attributeField.length); 329 330 Response response( 331 sizeof(pldm_msg_hdr) + PLDM_SET_BIOS_ATTR_CURR_VAL_RESP_BYTES, 0); 332 auto responsePtr = reinterpret_cast<pldm_msg*>(response.data()); 333 334 encode_set_bios_attribute_current_value_resp(request->hdr.instance_id, rc, 335 0, responsePtr); 336 337 return response; 338 } 339 340 } // namespace bios 341 } // namespace responder 342 } // namespace pldm 343