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