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