1 #include "bios_enum_attribute.hpp"
2 
3 #include "common/utils.hpp"
4 
5 #include <phosphor-logging/lg2.hpp>
6 
7 PHOSPHOR_LOG2_USING;
8 
9 using namespace pldm::utils;
10 
11 namespace pldm
12 {
13 namespace responder
14 {
15 namespace bios
16 {
17 BIOSEnumAttribute::BIOSEnumAttribute(const Json& entry,
18                                      DBusHandler* const dbusHandler) :
19     BIOSAttribute(entry, dbusHandler)
20 {
21     std::string attrName = entry.at("attribute_name");
22     Json pv = entry.at("possible_values");
23     for (auto& val : pv)
24     {
25         possibleValues.emplace_back(val);
26     }
27 
28     std::vector<std::string> defaultValues;
29     Json dv = entry.at("default_values");
30     for (auto& val : dv)
31     {
32         defaultValues.emplace_back(val);
33     }
34 
35     Json vdn = entry.at("value_names");
36     for (auto& val : vdn)
37     {
38         valueDisplayNames.emplace_back(val);
39     }
40     assert(defaultValues.size() == 1);
41     defaultValue = defaultValues[0];
42     if (dBusMap.has_value())
43     {
44         auto dbusValues = entry.at("dbus").at("property_values");
45         buildValMap(dbusValues);
46     }
47 }
48 
49 uint8_t BIOSEnumAttribute::getValueIndex(const std::string& value,
50                                          const std::vector<std::string>& pVs)
51 {
52     auto iter = std::find_if(pVs.begin(), pVs.end(),
53                              [&value](const auto& v) { return v == value; });
54     if (iter == pVs.end())
55     {
56         throw std::invalid_argument("value must be one of possible value");
57     }
58     return iter - pVs.begin();
59 }
60 
61 std::vector<uint16_t> BIOSEnumAttribute::getPossibleValuesHandle(
62     const BIOSStringTable& stringTable, const std::vector<std::string>& pVs)
63 {
64     std::vector<uint16_t> possibleValuesHandle;
65     for (const auto& pv : pVs)
66     {
67         auto handle = stringTable.findHandle(pv);
68         possibleValuesHandle.push_back(handle);
69     }
70 
71     return possibleValuesHandle;
72 }
73 
74 void BIOSEnumAttribute::buildValMap(const Json& dbusVals)
75 {
76     PropertyValue value;
77     size_t pos = 0;
78     for (auto it = dbusVals.begin(); it != dbusVals.end(); ++it, ++pos)
79     {
80         if (dBusMap->propertyType == "uint8_t")
81         {
82             value = static_cast<uint8_t>(it.value());
83         }
84         else if (dBusMap->propertyType == "uint16_t")
85         {
86             value = static_cast<uint16_t>(it.value());
87         }
88         else if (dBusMap->propertyType == "uint32_t")
89         {
90             value = static_cast<uint32_t>(it.value());
91         }
92         else if (dBusMap->propertyType == "uint64_t")
93         {
94             value = static_cast<uint64_t>(it.value());
95         }
96         else if (dBusMap->propertyType == "int16_t")
97         {
98             value = static_cast<int16_t>(it.value());
99         }
100         else if (dBusMap->propertyType == "int32_t")
101         {
102             value = static_cast<int32_t>(it.value());
103         }
104         else if (dBusMap->propertyType == "int64_t")
105         {
106             value = static_cast<int64_t>(it.value());
107         }
108         else if (dBusMap->propertyType == "bool")
109         {
110             value = static_cast<bool>(it.value());
111         }
112         else if (dBusMap->propertyType == "double")
113         {
114             value = static_cast<double>(it.value());
115         }
116         else if (dBusMap->propertyType == "string")
117         {
118             value = static_cast<std::string>(it.value());
119         }
120         else
121         {
122             error("Unknown D-Bus property type '{TYPE}'", "TYPE",
123                   dBusMap->propertyType);
124             throw std::invalid_argument("Unknown D-BUS property type");
125         }
126         valMap.emplace(value, possibleValues[pos]);
127     }
128 }
129 
130 uint8_t BIOSEnumAttribute::getAttrValueIndex()
131 {
132     auto defaultValueIndex = getValueIndex(defaultValue, possibleValues);
133     if (!dBusMap.has_value())
134     {
135         return defaultValueIndex;
136     }
137 
138     try
139     {
140         auto propValue = dbusHandler->getDbusPropertyVariant(
141             dBusMap->objectPath.c_str(), dBusMap->propertyName.c_str(),
142             dBusMap->interface.c_str());
143         auto iter = valMap.find(propValue);
144         if (iter == valMap.end())
145         {
146             return defaultValueIndex;
147         }
148         auto currentValue = iter->second;
149         return getValueIndex(currentValue, possibleValues);
150     }
151     catch (const std::exception&)
152     {
153         return defaultValueIndex;
154     }
155 }
156 
157 uint8_t BIOSEnumAttribute::getAttrValueIndex(const PropertyValue& propValue)
158 {
159     try
160     {
161         return getValueIndex(std::get<std::string>(propValue), possibleValues);
162     }
163     catch (const std::exception&)
164     {
165         return getValueIndex(defaultValue, possibleValues);
166     }
167 }
168 
169 void BIOSEnumAttribute::setAttrValueOnDbus(
170     const pldm_bios_attr_val_table_entry* attrValueEntry,
171     const pldm_bios_attr_table_entry* attrEntry,
172     const BIOSStringTable& stringTable)
173 {
174     if (!dBusMap.has_value())
175     {
176         return;
177     }
178     auto [pvHdls, _] = table::attribute::decodeEnumEntry(attrEntry);
179     auto currHdls = table::attribute_value::decodeEnumEntry(attrValueEntry);
180 
181     assert(currHdls.size() == 1);
182     auto valueString = stringTable.findString(pvHdls[currHdls[0]]);
183 
184     auto it = std::find_if(valMap.begin(), valMap.end(),
185                            [&valueString](const auto& typePair) {
186         return typePair.second == valueString;
187     });
188     if (it == valMap.end())
189     {
190         return;
191     }
192 
193     dbusHandler->setDbusProperty(*dBusMap, it->first);
194 }
195 
196 void BIOSEnumAttribute::populateValueDisplayNamesMap(uint16_t attrHandle)
197 {
198     for (auto& vdn : valueDisplayNames)
199     {
200         valueDisplayNamesMap[attrHandle].push_back(vdn);
201     }
202 }
203 
204 void BIOSEnumAttribute::constructEntry(
205     const BIOSStringTable& stringTable, Table& attrTable, Table& attrValueTable,
206     std::optional<std::variant<int64_t, std::string>> optAttributeValue)
207 {
208     auto possibleValuesHandle = getPossibleValuesHandle(stringTable,
209                                                         possibleValues);
210     std::vector<uint8_t> defaultIndices(1, 0);
211     defaultIndices[0] = getValueIndex(defaultValue, possibleValues);
212 
213     pldm_bios_table_attr_entry_enum_info info = {
214         stringTable.findHandle(name),         readOnly,
215         (uint8_t)possibleValuesHandle.size(), possibleValuesHandle.data(),
216         (uint8_t)defaultIndices.size(),       defaultIndices.data()};
217 
218     auto attrTableEntry = table::attribute::constructEnumEntry(attrTable,
219                                                                &info);
220     auto [attrHandle, attrType,
221           _] = table::attribute::decodeHeader(attrTableEntry);
222 
223     populateValueDisplayNamesMap(attrHandle);
224 
225     std::vector<uint8_t> currValueIndices(1, 0);
226 
227     if (optAttributeValue.has_value())
228     {
229         auto attributeValue = optAttributeValue.value();
230         if (attributeValue.index() == 1)
231         {
232             auto currValue = std::get<std::string>(attributeValue);
233             currValueIndices[0] = getValueIndex(currValue, possibleValues);
234         }
235         else
236         {
237             currValueIndices[0] = getAttrValueIndex();
238         }
239     }
240     else
241     {
242         currValueIndices[0] = getAttrValueIndex();
243     }
244 
245     table::attribute_value::constructEnumEntry(attrValueTable, attrHandle,
246                                                attrType, currValueIndices);
247 }
248 
249 int BIOSEnumAttribute::updateAttrVal(Table& newValue, uint16_t attrHdl,
250                                      uint8_t attrType,
251                                      const PropertyValue& newPropVal)
252 {
253     auto iter = valMap.find(newPropVal);
254     if (iter == valMap.end())
255     {
256         error("Failed to find index for new BIOS enum value '{VALUE}'", "VALUE",
257               std::get<std::string>(newPropVal));
258         return PLDM_ERROR;
259     }
260     auto currentValue = iter->second;
261     std::vector<uint8_t> handleIndices{
262         getValueIndex(currentValue, possibleValues)};
263     table::attribute_value::constructEnumEntry(newValue, attrHdl, attrType,
264                                                handleIndices);
265     return PLDM_SUCCESS;
266 }
267 
268 void BIOSEnumAttribute::generateAttributeEntry(
269     const std::variant<int64_t, std::string>& attributevalue,
270     Table& attrValueEntry)
271 {
272     attrValueEntry.resize(sizeof(pldm_bios_attr_val_table_entry) + 1);
273 
274     auto entry = reinterpret_cast<pldm_bios_attr_val_table_entry*>(
275         attrValueEntry.data());
276 
277     std::string value = std::get<std::string>(attributevalue);
278     entry->attr_type = 0;
279     entry->value[0] = 1; // number of current values, default 1
280     entry->value[1] = getAttrValueIndex(value);
281 }
282 
283 } // namespace bios
284 } // namespace responder
285 } // namespace pldm
286