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