1 #include "bios.hpp" 2 3 #include "utils.hpp" 4 #include "xyz/openbmc_project/Common/error.hpp" 5 6 #include <time.h> 7 8 #include <array> 9 #include <boost/crc.hpp> 10 #include <chrono> 11 #include <ctime> 12 #include <filesystem> 13 #include <iostream> 14 #include <memory> 15 #include <numeric> 16 #include <stdexcept> 17 #include <string> 18 #include <variant> 19 #include <vector> 20 21 namespace pldm 22 { 23 24 namespace responder 25 { 26 27 namespace utils 28 { 29 30 void epochToBCDTime(uint64_t timeSec, uint8_t& seconds, uint8_t& minutes, 31 uint8_t& hours, uint8_t& day, uint8_t& month, 32 uint16_t& year) 33 { 34 auto t = time_t(timeSec); 35 auto time = localtime(&t); 36 37 seconds = pldm::utils::decimalToBcd(time->tm_sec); 38 minutes = pldm::utils::decimalToBcd(time->tm_min); 39 hours = pldm::utils::decimalToBcd(time->tm_hour); 40 day = pldm::utils::decimalToBcd(time->tm_mday); 41 month = pldm::utils::decimalToBcd(time->tm_mon + 42 1); // The number of months in the range 43 // 0 to 11.PLDM expects range 1 to 12 44 year = pldm::utils::decimalToBcd(time->tm_year + 45 1900); // The number of years since 1900 46 } 47 48 std::time_t timeToEpoch(uint8_t seconds, uint8_t minutes, uint8_t hours, 49 uint8_t day, uint8_t month, uint16_t year) 50 { 51 struct std::tm stm; 52 53 stm.tm_year = year - 1900; 54 stm.tm_mon = month - 1; 55 stm.tm_mday = day; 56 stm.tm_hour = hours; 57 stm.tm_min = minutes; 58 stm.tm_sec = seconds; 59 stm.tm_isdst = -1; 60 61 // It will get the time in seconds since 62 // Epoch, 1970.1.1 00:00:00 +0000,UTC. 63 return timegm(&stm); 64 } 65 66 } // namespace utils 67 68 namespace bios 69 { 70 71 using EpochTimeUS = uint64_t; 72 73 DBusHandler dbusHandler; 74 75 Handler::Handler() : biosConfig(BIOS_JSONS_DIR, BIOS_TABLES_DIR, &dbusHandler) 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_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 hostTimePath = "/xyz/openbmc_project/time/host"; 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 hostTimePath, "Elapsed", timeInterface); 123 } 124 catch (const sdbusplus::exception::SdBusError& e) 125 { 126 std::cerr << "Error getting time, PATH=" << hostTimePath 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 setTimeInterface = "xyz.openbmc_project.Time.EpochTime"; 161 constexpr auto setTimePath = "/xyz/openbmc_project/time/host"; 162 constexpr auto timeSetPro = "Elapsed"; 163 164 auto rc = decode_set_date_time_req(request, payloadLength, &seconds, 165 &minutes, &hours, &day, &month, &year); 166 if (rc != PLDM_SUCCESS) 167 { 168 return ccOnlyResponse(request, rc); 169 } 170 timeSec = pldm::responder::utils::timeToEpoch(seconds, minutes, hours, day, 171 month, year); 172 uint64_t timeUsec = std::chrono::duration_cast<std::chrono::microseconds>( 173 std::chrono::seconds(timeSec)) 174 .count(); 175 PropertyValue value{timeUsec}; 176 try 177 { 178 DBusMapping dbusMapping{setTimePath, setTimeInterface, timeSetPro, 179 "uint64_t"}; 180 pldm::utils::DBusHandler().setDbusProperty(dbusMapping, value); 181 } 182 catch (std::exception& e) 183 { 184 185 std::cerr << "Error Setting time,PATH=" << setTimePath 186 << "TIME INTERFACE=" << setTimeInterface 187 << "ERROR=" << e.what() << "\n"; 188 189 return ccOnlyResponse(request, PLDM_ERROR); 190 } 191 192 return ccOnlyResponse(request, PLDM_SUCCESS); 193 } 194 195 Response Handler::getBIOSTable(const pldm_msg* request, size_t payloadLength) 196 { 197 uint32_t transferHandle{}; 198 uint8_t transferOpFlag{}; 199 uint8_t tableType{}; 200 201 auto rc = decode_get_bios_table_req(request, payloadLength, &transferHandle, 202 &transferOpFlag, &tableType); 203 if (rc != PLDM_SUCCESS) 204 { 205 return ccOnlyResponse(request, rc); 206 } 207 208 auto table = 209 biosConfig.getBIOSTable(static_cast<pldm_bios_table_types>(tableType)); 210 if (!table) 211 { 212 return ccOnlyResponse(request, PLDM_BIOS_TABLE_UNAVAILABLE); 213 } 214 215 Response response(sizeof(pldm_msg_hdr) + 216 PLDM_GET_BIOS_TABLE_MIN_RESP_BYTES + table->size()); 217 auto responsePtr = reinterpret_cast<pldm_msg*>(response.data()); 218 219 rc = encode_get_bios_table_resp( 220 request->hdr.instance_id, PLDM_SUCCESS, 0 /* nxtTransferHandle */, 221 PLDM_START_AND_END, table->data(), response.size(), responsePtr); 222 if (rc != PLDM_SUCCESS) 223 { 224 return ccOnlyResponse(request, rc); 225 } 226 227 return response; 228 } 229 230 Response Handler::getBIOSAttributeCurrentValueByHandle(const pldm_msg* request, 231 size_t payloadLength) 232 { 233 uint32_t transferHandle; 234 uint8_t transferOpFlag; 235 uint16_t attributeHandle; 236 237 auto rc = decode_get_bios_attribute_current_value_by_handle_req( 238 request, payloadLength, &transferHandle, &transferOpFlag, 239 &attributeHandle); 240 if (rc != PLDM_SUCCESS) 241 { 242 return ccOnlyResponse(request, rc); 243 } 244 245 auto table = biosConfig.getBIOSTable(PLDM_BIOS_ATTR_VAL_TABLE); 246 if (!table) 247 { 248 return ccOnlyResponse(request, PLDM_BIOS_TABLE_UNAVAILABLE); 249 } 250 251 auto entry = pldm_bios_table_attr_value_find_by_handle( 252 table->data(), table->size(), attributeHandle); 253 if (entry == nullptr) 254 { 255 return ccOnlyResponse(request, PLDM_INVALID_BIOS_ATTR_HANDLE); 256 } 257 258 auto entryLength = pldm_bios_table_attr_value_entry_length(entry); 259 Response response(sizeof(pldm_msg_hdr) + 260 PLDM_GET_BIOS_ATTR_CURR_VAL_BY_HANDLE_MIN_RESP_BYTES + 261 entryLength, 262 0); 263 auto responsePtr = reinterpret_cast<pldm_msg*>(response.data()); 264 rc = encode_get_bios_current_value_by_handle_resp( 265 request->hdr.instance_id, PLDM_SUCCESS, 0, PLDM_START_AND_END, 266 reinterpret_cast<const uint8_t*>(entry), entryLength, responsePtr); 267 if (rc != PLDM_SUCCESS) 268 { 269 return ccOnlyResponse(request, rc); 270 } 271 272 return response; 273 } 274 275 Response Handler::setBIOSAttributeCurrentValue(const pldm_msg* request, 276 size_t payloadLength) 277 { 278 uint32_t transferHandle; 279 uint8_t transferOpFlag; 280 variable_field attributeField; 281 282 auto rc = decode_set_bios_attribute_current_value_req( 283 request, payloadLength, &transferHandle, &transferOpFlag, 284 &attributeField); 285 if (rc != PLDM_SUCCESS) 286 { 287 return ccOnlyResponse(request, rc); 288 } 289 290 rc = biosConfig.setAttrValue(attributeField.ptr, attributeField.length); 291 292 return ccOnlyResponse(request, rc); 293 } 294 295 } // namespace bios 296 } // namespace responder 297 } // namespace pldm 298