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