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